diff --git a/OsmAnd-java/OsmAnd-core-android.jar b/OsmAnd-java/OsmAnd-core-android.jar deleted file mode 100644 index e90615a1c7..0000000000 Binary files a/OsmAnd-java/OsmAnd-core-android.jar and /dev/null differ diff --git a/OsmAnd-java/OsmAnd-core.jar b/OsmAnd-java/OsmAnd-core.jar deleted file mode 100644 index 7e621009fe..0000000000 Binary files a/OsmAnd-java/OsmAnd-core.jar and /dev/null differ diff --git a/OsmAnd-java/src/main/java/net/osmand/data/MapObject.java b/OsmAnd-java/src/main/java/net/osmand/data/MapObject.java index d3700896b2..5fb13d62ac 100644 --- a/OsmAnd-java/src/main/java/net/osmand/data/MapObject.java +++ b/OsmAnd-java/src/main/java/net/osmand/data/MapObject.java @@ -89,23 +89,22 @@ public abstract class MapObject implements Comparable { names.putAll(name); } } - + public Map getNamesMap(boolean includeEn) { - if (!includeEn || Algorithms.isEmpty(enName)) { - if (names == null) { - return Collections.emptyMap(); - } - return names; + if ((!includeEn || Algorithms.isEmpty(enName)) && names == null) { + return Collections.emptyMap(); } Map mp = new HashMap(); if (names != null) { - Iterator> it = mp.entrySet().iterator(); - while(it.hasNext()) { + Iterator> it = names.entrySet().iterator(); + while (it.hasNext()) { Entry e = it.next(); mp.put(e.getKey(), unzipContent(e.getValue())); } } - mp.put("en", unzipContent(enName)); + if (includeEn && !Algorithms.isEmpty(enName)) { + mp.put("en", unzipContent(enName)); + } return mp; } @@ -115,7 +114,7 @@ public abstract class MapObject implements Comparable { l.add(unzipContent(enName)); } if (names != null) { - for(String nm : names.values()) { + for (String nm : names.values()) { l.add(unzipContent(nm)); } } diff --git a/OsmAnd/res/values-ar/strings.xml b/OsmAnd/res/values-ar/strings.xml index 1b5757ad14..54a8a6f3dd 100644 --- a/OsmAnd/res/values-ar/strings.xml +++ b/OsmAnd/res/values-ar/strings.xml @@ -4005,4 +4005,5 @@ \n ملف تعريف أوسماند ملف تعريف المستخدم + عكس جميع النقاط \ No newline at end of file diff --git a/OsmAnd/res/values-ca/strings.xml b/OsmAnd/res/values-ca/strings.xml index 1e6d6cfef8..1b1794113a 100644 --- a/OsmAnd/res/values-ca/strings.xml +++ b/OsmAnd/res/values-ca/strings.xml @@ -1044,7 +1044,7 @@ Quant a Informació de la versió, llicències, membres del projecte Escalats baixats: %1$s - Expira (minuts): %1$s + Marge de caducitat (minuts): %1$s Baixable: %1$s Escalat màxim: %1$s Escalat mínim: %1$s @@ -1069,7 +1069,7 @@ El càlcul ràpid de ruta ha fallat (%s), retorn al mètode lent. Desactiva el càlcul de ruta en dues fases per navegació amb cotxe. Desactiva el càlcul complex de la ruta - El proveïdor de tessel·les %1$s estava desat + Proveïdor de tessel·les %1$s desat Selecciona existent… Info depuració FPS Ordre optimitzat de les fites de la ruta fins a la destinació. @@ -1728,7 +1728,7 @@ PMF Preguntes més freqüents Visualització del mapa - Buscant el mapa + Buscant al mapa Instal·lació i solució de problemes Articles tècnics Versions @@ -1944,7 +1944,7 @@ Freqüència dels recordatoris Interval mínim de temps entre anuncis. Desviació relativa - No s\'ha fixat la destinació + Connector d\'accessibilitat: No s\'ha establert destinació Desviació respecte el Nord magnètic Activa la navegació pels canvis a OsmAnd Live. Navegació OsmAnd Live @@ -2386,7 +2386,7 @@ Mostra començant pel nivell d\'escala Permet l\'accés privat Permet l\'accés a zones privades. - Baixeu el mapa \'Ombrejat de relleu\' per veure ombrejat segons alçada. + Baixeu el mapa de superposició \'Ombrejat de relleu\' per veure ombrejat segons alçada. Instal·leu el connector \'Corbes de nivell\' per veure àrees graduades verticalment. Connector Compreu i instal·leu el connector \'Corbes de nivell\' per visualitzar una gradació vertical d\'àrees. @@ -3171,7 +3171,7 @@ Parrot Esquí de ruta Itineraris d\'esquí de ruta. - Camp a través i fora pistes són vies no oficials. Normalment so estan condicionades ni mantingudes per treballadors ni revisades per la nit. Si hi entreu en sereu responsables del que passi. + \'Camp a través\' i \'Fora pistes\' són vies no oficials. Normalment no estan regulades ni mantingudes ni revisades de nit. Entrant-hi, sereu responsables del que passi. Activa pantalla si cal maniobrar Per utilitzar aquest paràmetre heu d\'activar almenys un perfil d\'aplicació. Carretera sobre la neu @@ -3676,7 +3676,7 @@ metres Suprimeix la fita més propera Proporcioneu un nom per al punt - La propera fita de la ruta s\'esborrarà. Si es tractés de la Destinació final, la navegació s\'aturaria. + Esborra la propera fita de la vostra ruta. Si aquesta fos la Destinació, la navegació s\'aturaria. Baixa mapes de la Viquipèdia Obteniu informació de punts d\'interès des la Viquipèdia. Serà la vostra guia de butxaca en desconnexió - només cal que activeu el connector Viquipèdia i fruireu dels articles que tracten de les coses que us rodegen. Seleccioneu el criteri limitant desitjat: temps o distància. @@ -3710,7 +3710,7 @@ Ruta inversa La ruta sencera serà recalculada fent ús del perfil seleccionat. Només el següent segment serà recalculat fent ús del perfil seleccionat. - Ruta sencera + Pista sencera Següent segment Llindar de distància Perfil de navegació @@ -3788,7 +3788,7 @@ Torna a fer Manté la pantalla desactivada Moto enduro - Esteu segur que voleu descartar totes les modificacions de la ruta planificada en tancar-la\? + Segur que voleu descartar totes les modificacions de la ruta planificada\? Afegeix a un fitxer de traces Traces Enregistra el trajecte a un fitxer GPX @@ -3796,7 +3796,7 @@ Afegeix una adreça Nom del fitxer REC - Aturarà l\'enregistrament de traces quan es mati l\'aplicació (mitjançant aplicacions recents). (La indicació de fons OsmAnd desapareix de la barra de notificacions d\'Android.) + L\'enregistrament de traces s\'aturarà quan es mati l\'aplicació (mitjançant aplicacions recents). (Les indicacions en segon pla d\'OsmAnd desapareixeran de la barra de notificacions Android.) Trieu l\'interval d\'enregistrament general dels desplaçaments (s\'activa mitjançant el giny del mapa \'Enregistrament de ruta\'). Traça simplificada Només es desarà la línia del trajecte, les fites s\'esborraran. @@ -3878,7 +3878,7 @@ Afegeix foto Registreu-vos a \nOpenPlaceReviews.org - Les fotos les proporciona el projecte OpenPlaceReviews.org de dades obertes. Per poder pujar les vostres fotos, cal que us registreu al lloc web. + Les fotos les proporciona el projecte de dades obertes OpenPlaceReviews.org. Per poder pujar les vostres fotos, cal que us registreu al lloc web. Crea un compte nou ja tinc un compte Historial de cerques @@ -3900,10 +3900,10 @@ OsmAnd mostra fotos de diverses fonts: \nOpenPlaceReviews - fotos de PDIs; \nMapillary: imatges a nivell de carrer; -\nWeb / Wikimedia: fotos dels PDIs especificats a les dades d\'OpenStreetMap. +\nWeb / Wikimedia: fotos dels PDIs d\'OpenStreetMap. %1$s * %2$s Equipament - Podeu utilitzar les dades d’elevació per tenir en compte l\'ascens / descens de la vostra ruta + Podeu utilitzar les dades d’elevació per acumular l\'ascens/descens de la vostra ruta Avioneta \"Identificable\" vol dir que la traça es mostrarà públicament a les vostres traces GPS i als llistats públics de traces GPS, és a dir, els altres usuaris la podran baixar en brut i associada al vostre usuari. Les dades cronològiques de pas per les fites de l’API GPS que es publiquen mitjançant l’API de fites faran referència a la pàgina de la vostra traça original. \"Privada\" vol dir que la traça no es mostra a cap llistat públic, però les fites que conté estan disponibles a través de l\'API pública GPS, desordenades i sense enregistraments temporals. @@ -3911,4 +3911,18 @@ Separa abans Separa després Afegeix un segment nou + Alemany (col·loquial) + Novetats + Perfil OsmAnd + Perfil d\'usuari + • S\'ha afegit una opció per exportar i importar totes les dades, incloent configuracions, recursos i preferits +\n +\n • Planificació de la ruta: gràfics per segments de traçat de la ruta i s\'afegeix la possibilitat de crear i editar diversos segments de la traça alhora +\n +\n • S\'ha afegit el mètode d\'autenticació OAuth per a OpenStreetMap i s\'ha millorat la interfície d\'usuari dels diàlegs OSM +\n +\n • S\'admeten colors personalitzats per a llocs preferits i fites +\n +\n + Revertir tots els punts \ No newline at end of file diff --git a/OsmAnd/res/values-de/phrases.xml b/OsmAnd/res/values-de/phrases.xml index 5406e3a1e1..e39de1583c 100644 --- a/OsmAnd/res/values-de/phrases.xml +++ b/OsmAnd/res/values-de/phrases.xml @@ -557,7 +557,7 @@ Tapezierer Staubsaugergeschäft Kleinkaufhaus - Fahrzeugkontrolle + Fahrzeugprüfstelle Fahrzeugrampe Verkaufsautomat Tierarzt diff --git a/OsmAnd/res/values-de/strings.xml b/OsmAnd/res/values-de/strings.xml index 7ec31aa8a6..c5e4bb3130 100644 --- a/OsmAnd/res/values-de/strings.xml +++ b/OsmAnd/res/values-de/strings.xml @@ -4023,4 +4023,6 @@ OsmAnd-Profil Benutzerprofil Alle Punkte umkehren + Wählen Sie das Profil, das beim Start der Anwendung verwendet werden soll. + Zuletzt verwendet \ No newline at end of file diff --git a/OsmAnd/res/values-eo/phrases.xml b/OsmAnd/res/values-eo/phrases.xml index 7949fb2046..670f41c2be 100644 --- a/OsmAnd/res/values-eo/phrases.xml +++ b/OsmAnd/res/values-eo/phrases.xml @@ -3889,4 +3889,6 @@ puto tuba (abisena) Pezilo por aŭtomobiloj Oficejo de naturrezervejo + Vestaĵ-lavejo (ĉe rivero) + Stacio de rubmastrumado \ No newline at end of file diff --git a/OsmAnd/res/values-eo/strings.xml b/OsmAnd/res/values-eo/strings.xml index 563bf3a276..09a2f3363d 100644 --- a/OsmAnd/res/values-eo/strings.xml +++ b/OsmAnd/res/values-eo/strings.xml @@ -4017,4 +4017,5 @@ \n OsmAnd‑profilo Profilo de uzanto + Inversigi ĉiujn punktojn \ No newline at end of file diff --git a/OsmAnd/res/values-et/phrases.xml b/OsmAnd/res/values-et/phrases.xml index 8ef9bbe198..1465cf57c8 100644 --- a/OsmAnd/res/values-et/phrases.xml +++ b/OsmAnd/res/values-et/phrases.xml @@ -3857,4 +3857,27 @@ Mobiiliraha müügikoht Pargivahi maja Autokaal + Riigiülesed + Esindus + Kontor + Aukonsulaat + Peakonsulaat + Konsulaaresindus + Konsulaarasutus + Juhib konsul + Missioon + Huvide jaotis + Kõrge komisjonitasu + Delegatsioon + Filiaal + Juhib suursaadik + Sidepidamine + Kodanike teenused + Immigrandi viisad + Mitteimmigrandi viisad + Sidepidamine + Jah + Ei + Jäätmejaam + Avalik pesumaja \ No newline at end of file diff --git a/OsmAnd/res/values-et/strings.xml b/OsmAnd/res/values-et/strings.xml index cd68aefcae..840a2cf452 100644 --- a/OsmAnd/res/values-et/strings.xml +++ b/OsmAnd/res/values-et/strings.xml @@ -7,8 +7,8 @@ Jäta vahele lisa Sõnum - A-GPS andmed on allalaetud: %1$s - Kaartide allalaadimine + A-GPS andmed on alla laaditud: %1$s + Laadi kaarte Tere tulemast Märgi kustutamiseks Tühjendada ajalugu\? @@ -46,9 +46,9 @@ Ära enam näita Mäleta valikut Värskenda - Lae alla + Laadi alla Allalaadimine… - Alla laetud + Alla laaditud Sulge Välju Näita @@ -109,7 +109,7 @@ Valitud lugu Aktiveeri OsmAnd Live muutuste navigeerimine. OsmAnd Live navigeerimine - Sihtkoht määramata + Juurdepääsetavuse pistikprogramm: sihtkoht määramata Asimuut Suhteline kurss Ära otsi uut teekonda kui eelmiselt lahkuti @@ -154,7 +154,7 @@ Lisa fotosid Mapillary vidin Lähtesta - Lae uuesti + Laadi uuesti Vale kasutajanimi Kuupäev Sisesta kasutajatunnus @@ -186,7 +186,7 @@ Link avatakse veebilehitsejas. Kuidas link avada? Loe Wikipediat ilma võrguühenduseta - Lae kõik alla + Laadi kõik alla Rakenduse taaskäivitamine Näita pilte Kaardid, mida vajad @@ -195,7 +195,7 @@ Makstud rakendus Makstud lisa Uuendus saadaval - Lae fail alla + Laadi fail alla Alusta muutmist Hangi piiramatu ligipääs Igakuised kaardiuuendused @@ -271,7 +271,7 @@ Arvutatud teekond on tühi. Kasuta internetti teekonna arvutamiseks. Tark teekonna arvutamine - Arvuta uuesti ainult teekonna algusosa. Seda võid kasutada pikemate vahemaade korral. + Arvutab ümber ainult teekonna algosa, mis on kasulik pikkade reiside korral. Simuleeri arvutatud või salvestatud GPX teekonda. Künkavarjutuse kiht keelatud Ratsutamisteed @@ -312,7 +312,7 @@ Kuva alates suurenduse tasemest Luba eramaad Lisand - Lae alla \'Künkavarjutuse ülekatte\' kaart vertikaalse varjutuse kuvamiseks. + Vertikaalse varjutuse kuvamiseks laadi alla „Künkavarjutuse ülekatte“ kaart. Paigalda \'Kõrgusjoonte\' laiendus astmestatud vertikaalsete alade kuvamiseks. Peida alates suurenduse tasemest Sorteeritud kauguse järgi @@ -513,13 +513,13 @@ Ära Tee Ainult Wi-Fi ühendusega - Lae pildid alla - Artikli pilte saab laadida alla võrguühenduseta kasutamiseks. + Laadi pildid alla + Artikli pilte saad laadida alla võrguühenduseta kasutamiseks. \nAlati saadaval menüüs \'Avasta\' → \'Valikud\'. Ainult Wi-Fi ühendusega Vali reisikiri Reisikiri - Lae pildid alla + Laadi pildid alla Vali plaan Vali sobiv toode Ostmise järel on sulle püsivalt kasutatav. @@ -528,11 +528,11 @@ Vikipeedia võrguühenduseta Ava kõik OsmAnd funktsioonid Uued Wikivoyage andmed saadaval, uuenda nautimiseks. - Lae alla Wikivoyage reisijuhid, et vaadata artikleid kohtadest üle maailma ilma internetiühenduseta. + Laadi alla Wikivoyage reisijuhid, et vaadata artikleid kohtadest üle maailma ilma internetiühenduseta. Tasuta ülemaailmne kõigi poolt täiendatav reisijuht. Tere tulemast avatud beetasse - Kõrgusjooned & künkavarjutusega kaardid - Sinu järjehoidjatesse lisatud artiklite alusel on soovitatav laadida alla järgmised kaardid: + Kõrgusjooned ja künkavarjutusega kaardid + Sinu järjehoidjatesse lisatud artiklite alusel on soovitame alla laadida järgmised kaardid: Oled tühistanud oma OsmAnd Live tellimuse Uuenda tellimust kõigi funktsioonide kasutamise jätkamiseks: Hangi OsmAnd Live tellimus, et lugeda Vikipeedia ja Wikivoyage artikleid ilma võrguühenduseta. @@ -702,7 +702,7 @@ \n \nSeadista mis tahes ajal menüüst \'Seaded\' → \'Privaatsus ja turvalisus\'. Vali, millist liiki andmeid soovid jagada: - Alla laetud kaardid + Allalaaditud kaardid Külastatud ekraanid Määra, milliseid andmeid OsmAnd võib jagada. Aita meil mõista riikide ja piirkondade kaartide populaarsust. @@ -913,7 +913,7 @@ Logcati puhver Laienduse seaded Vaikimisi - Selle ala vaatlemiseks lae alla üksikasjalik %s kaart. + Selle ala vaatlemiseks laadi alla üksikasjalik %s kaart. Teisaldada OsmAnd andmefailid uude sihtkohta\? \n%1$s > %2$s %1$s • %2$s @@ -1033,7 +1033,7 @@ %1$s • %2$s %1$s, %2$s Isiklik - Allalaadimine %s + Laadin alla %s Jäme Kõrbete ja muude hõredalt asustatud piirkondade jaoks. Üksikasjalikum. Liikumisel kuvatav asukohaikoon @@ -1124,7 +1124,7 @@ Eesti Sebu Sul on vana ja kokkusobimatu Vikipeedia andmestik. Arhiveerida see\? - Laadida täiendavaid andmeid Vikipeediast (%1$s MB)\? + Kas laadime täiendavaid andmeid Vikipeediast (%1$s MB)\? Asukohateenus on välja lülitatud. Lülitada sisse\? Väldi iseseisvat logimist OsmAnd import @@ -1239,9 +1239,9 @@ Ameerika teede atlas Puuduvad v1.9 teekonna arvutuse reeglid Ära kasuta v1.9 teekonna arvutuse reegleid. - Laadida alla võrguühenduseta kaarte\? + Kas laadime alla võrguühenduseta kaarte\? Oled laadinud alla %1$s kaarti - Lae alla uus kaart + Laadi alla uus kaart Halda Kaardi keel Peatuskohad @@ -1323,7 +1323,7 @@ Kaugus Ringkäik Kõik - Laadida alla puuduvad kaardid %1$s (%2$d MB)\? + Kas laadime alla puuduvad kaardid %1$s (%2$d MB)\? Sirvi kaarti Auto Jalgratas @@ -1511,9 +1511,9 @@ Maailma aluskaart Versioon: Versiooni info, litsentsid, projekti liikmed - Allalaetud suumitasemed: %1$s + Allalaaditud suumitasemed: %1$s Aegumine (minutites): %1$s - Alla laetav: %1$s + Alla laaditav: %1$s Maksimaalne suurendus: %1$s Minimaalne suurendus: %1$s Mercatori elliptiline projektsioon @@ -1570,7 +1570,7 @@ Kasuta magnetandurit Muu %1$d faili järel - %1$d faili veel alla laadida + Veel %1$d faili on jäänud alla laadida Täisversioon Lõpeta navigeerimine Eemalda sihtpunkt @@ -1697,37 +1697,37 @@ Hääljuhised peatavad muusika esitamise. Peata muusika OsmAnd kaardid ja navigeerimine - OsmAnd (OSM automatiseeritud navigeerimisjuhised) -\n -\n OsmAnd on avatud lähtekoodiga tarkvara navigeerimisrakendus, millel on juurdepääs mitmesugustele ülemaailmsetele OpenStreetMap (OSM) andmetele. Kõiki kaardiandmeid (vektor- või paanikaarte) saab salvestada telefoni mälukaardile võrguühenduseta kasutamiseks. Pakutakse ka võrguühenduseta ja veebipõhist teekonna leidmise funktsiooni, sealhulgas pöörangupõhiseid hääljuhiseid. -\n -\n Mõned põhifunktsioonid: -\n - Täielik võrguühenduseta funktsioon (salvestage alla laetud vektor- või paanikaardid seadme mällu) -\n - Saadaval on kompaktsed võrguühenduseta vektorkaardid üle kogu maailma -\n - Riigi või piirkonna kaardid on alla laetavad otse rakendusest -\n - Võimalik on mitme kaardikihi ülekatmine, näiteks GPX või navigeerimisrajad, huvipunktid, eelistused, kõrgusjooned, ühistranspordi peatused, täiendavad kaardid kohandatava läbipaistvusega -\n - Aadresside ja kohtade (huvipunktide) otsing ilma võrguühenduseta -\n - Võrguühenduseta teekonna leidmine keskmise vahemaa jaoks -\n - Auto-, jalgratta- ja jalakäijarežiim valikuliselt: -\n - Automatiseeritud päeva/öö vaate vahetamine -\n - Kiirusest sõltuv kaardi suurendusaste -\n - Kaardi joondamine vastavalt kompassile või liikumissuunale -\n - Teekonnal juhtimine, kiiruspiirangu kuvamine, salvestatud ja kõnesünteesitud hääljuhised -\n -\n Selle tasuta OsmAnd versiooni piirangud: -\n - Kaardi allalaadimiste arv on piiratud -\n - Vikipeedia huvipunktidele puudub võrguühenduseta juurdepääs -\n + OsmAnd (OSM automatiseeritud navigeerimisjuhised) +\n +\n OsmAnd on avatud lähtekoodiga tarkvara navigeerimisrakendus, millel on juurdepääs mitmesugustele ülemaailmsetele OpenStreetMap (OSM) andmetele. Kõiki kaardiandmeid (vektor- või paanikaarte) saad salvestada telefoni mälukaardile võrguühenduseta kasutamiseks. Olemas on ka võrguühenduseta ja veebipõhised teekonna leidmise funktsioonid, sealhulgas pöörangupõhiseid hääljuhiseid. +\n +\n Mõned põhifunktsioonid: +\n - Täielik võrguühenduseta funktsioon (salvestades allalaaditud vektor- või paanikaardid seadme mällu) +\n - Saadaval on kompaktsed võrguühenduseta vektorkaardid üle kogu maailma +\n - Riigi või piirkonna kaardid on alla laaditavad otse rakendusest +\n - Võimalik on mitme kaardikihi ülekatmine, näiteks GPX või navigeerimisrajad, huvipunktid, eelistused, kõrgusjooned, ühistranspordi peatused, täiendavad kaardid kohandatava läbipaistvusega +\n - Aadresside ja kohtade (huvipunktide) otsing ilma võrguühenduseta +\n - Võrguühenduseta teekonna leidmine keskmise vahemaa jaoks +\n - Auto-, jalgratta- ja jalakäijarežiim valikuliselt: +\n - Automatiseeritud päeva/öö vaate vahetamine +\n - Kiirusest sõltuv kaardi suurendusaste +\n - Kaardi joondamine vastavalt kompassile või liikumissuunale +\n - Teekonnal juhtimine, kiiruspiirangu kuvamine, salvestatud ja kõnesünteesitud hääljuhised +\n +\n Selle tasuta OsmAnd versiooni piirangud: +\n - Kaardi allalaadimiste arv on piiratud +\n - Vikipeedia huvipunktidele puudub võrguühenduseta juurdepääs +\n \n OsmAnd on aktiivselt arendamisel ning meie projekt ja selle jätkuv edasiminek sõltub rahalisest toetusest uute funktsioonide väljatöötamiseks ja testimiseks. Palun kaalu OsmAnd+ ostmist või konkreetsete uute funktsioonide rahastamist või üldise annetuse tegemist leheküljel https://osmand.net. OsmAnd+ kaardid ja navigeerimine OsmAnd+ (OSM automatiseeritud navigeerimisjuhised) \n -\n OsmAnd+ on avatud lähtekoodiga tarkvara navigeerimisrakendus, millel on juurdepääs paljudele globaalsetele OpenStreetMap (OSM) andmetele. Kõiki kaardiandmeid (vektor- ja paanikaarte) saab salvestada telefoni mälukaardile võrguühenduseta kasutamiseks. Võimalik on nii võrguühenduseta kui ka -ühendusega teekonna leidmise funktsioon, milles on ka hääljuhiseid. +\n OsmAnd+ on avatud lähtekoodiga tarkvara navigeerimisrakendus, millel on juurdepääs paljudele globaalsetele OpenStreetMap (OSM) andmetele. Kõiki kaardiandmeid (vektor- ja paanikaarte) saad salvestada telefoni mälukaardile võrguühenduseta kasutamiseks. Võimalik on nii võrguühenduseta kui ka -ühendusega teekonna leidmise funktsioon, milles on ka hääljuhiseid. \n -\n OsmAnd+ on rakenduse tasuline versioon, mida ostes toetate projekti, rahastate uute funktsioonide arendamist ja saate kõige värskemaid uuendusi. +\n OsmAnd+ on rakenduse tasuline versioon, mida ostes toetad projekti, rahastad uute funktsioonide arendamist ja saad kõige värskemaid uuendusi. \n \n Mõned põhifunktsioonid: -\n - Täielik võrguühenduseta funktsioon (salvestage alla laetud vektor- ja paanikaardid seadme mällu) +\n - Täielik võrguühenduseta funktsioon (salvesta alla laaditud vektor- ja paanikaardid seadme mällu) \n - Saadaval on kompaktsed kogu maailma vektorkaardid (võrguühenduseta režiim) \n - Riigi või piirkonna kaartide piiramatu allalaadimine otse rakendusest \n - Võrguühenduseta Vikipeedia funktsioon (laadige Vikipeedia huvipunktid alla), sobib suurepäraselt vaatamisväärsustega tutvumiseks @@ -1832,11 +1832,11 @@ Lähtepunkt on veel määramata. Asukoht ei ole veel teada. Määra läbipaistvus (0 - läbipaistev, 255 - läbipaistmatu) - Tühistada allalaadimine\? - Põhifunktsionaalsuse pakkumiseks vajaliku aluskaardi alla laadimine on järjekorras. + Tühistame allalaadimise\? + Põhifunktsionaalsuse pakkumiseks vajaliku aluskaardi allalaadimine on järjekorras. Standardkaardid (vektor) Luba \'Internetikaardid\' lisa erinevate kaardiallikate valimiseks - Kasuta internetikaarte (lae alla ja puhverda paanid mälukaardil). + Kasuta internetikaarte (laadi alla ja puhverda paanid mälukaardil). Vali veebipõhised või puhverdatud paanikaartide allikad. Lisad Lisad @@ -1901,11 +1901,11 @@ Kui vara soovid kohale jõudmise teavitust\? Helkivad ülekatted Võrguühenduseta muutmine - Kui võrguühenduseta muutmine on kasutusel, siis muudatused salvestatakse esmalt kohalikus nutiseadmes ning laetakse üles eraldi päringuga. Muul juhul laetakse muudatused üles koheselt. + Kui võrguühenduseta muutmine on kasutusel, siis muudatused salvestatakse esmalt kohalikus nutiseadmes ning laaditakse üles eraldi päringuga. Muul juhul laaditakse muudatused üles koheselt. Üles laadimine… - {0} huvipunkti/märget üles laetud - Lae kõik üles - Lae muudatus OSM-i üles + {0} huvipunkti/märget üles laaditud + Laadi kõik üles + Laadi muudatus OSM-i üles Kustuta muudatus Asünkroonne OSM muutmine: OSM huvipunktid/märkmed salvestatud seadmes @@ -1946,7 +1946,7 @@ Salasõna Taustarežiim OsmAnd töötab taustal, ekraan välja lülitatud. - Kas laeme alla {0} fail(i)\? + Kas laadime alla {0} fail(i)\? \n{1} MB (kokku {2} MB-st) võetakse kasutusse. Läbipaistev teema Kaardivaade automaatselt keskele @@ -1981,7 +1981,7 @@ Otsi lähedusest Failinimi: Sama nimega fail on juba olemas. - %1$d / %2$d ühik(ut) üles laetud. + %1$d / %2$d ühik(ut) üles laaditud. Saada OSM-i Kuva kaardil rohkem üksikasju Lemmikpunktid kustutatud. @@ -2017,7 +2017,7 @@ Veebipõhine navigeerimine ei tööta ilma võrguühenduseta. Mittetoetatud keel Puuduvad andmed - Mine turule valitud keele alla laadimiseks\? + Kas läheme valitud keele alla laadimiseks rakendusepoodi\? Raja vastupidine suund Kasuta olemasoleva sihtkohta Mööda kogu rada @@ -2105,16 +2105,16 @@ Päikesetõus/loojang Valgusandur Päeva/öö režiim - Laadida alla {0} fail(s) ({1} MB)\? + Kas laadime alla {0} fail(i) ({1} MB)\? {0} ühik(ut) valitud - Alla laetud - Lae kaart alla + Alla laaditud + Laadi kaart alla Jätkuv kuvamine Jätkuv kuvamine pilt-pildi haaval kuvamise asemel. Valitud piirkonna joonistamine ebaõnnestus. Kasuta asukohta… - Kuvaja laetud - Kuvaja laadimine ebaõnnestus. + Visualiseerija laetud + Visualiseerija laadimine ebaõnnestus. Vektorkuvaja Vali kuvamise välimus Kuva huvipunkti veebileht @@ -2128,7 +2128,7 @@ Lähtesta transpordi otsing Salvestatud hääl Hääljuhised - Vektorkaarte ei laetud + Vektorkaarte ei laaditud GPX andmete laadimine ebaõnnestus. Võrguühenduseta vektorkaardid Otsi transporti peatuses @@ -2166,7 +2166,7 @@ Ära sirvi veebikaartide paane sellest suuremate suumitasemete korral. Veebipõhine või võrguühenduseta navigeerimisteenus. Asenda - Tänavate/hoonete laadimine… + Laadin tänavaid/hooneid… Kasuta veebipõhist navigeerimist Uuenda kaart Vali hoone @@ -2219,14 +2219,14 @@ Jaga asukohta Saada \'Väljas\' käivitab kaardi otse. - Kaart alla laetud + Kaart on allalaaditud %1$s kaart on kasutusvalmis. Kuva kaart QR kood - Maailma aluskaart (katab kogu maailma madalal suurendustasemel) puudub või on aegunud. Palun kaalu selle allalaadimist üldise ülevaate saamiseks. + Maailma aluskaart (katab kogu maailma madalal suurendustasemel) puudub või on aegunud. Üldise ülevaate saamiseks palun kaalu selle allalaadimist. Rakenduse teave OSM huvipunkt loodud - Lae üles + Laadi üles Uuenda Välja pakutud objektid Loe lisa @@ -2288,7 +2288,7 @@ Vali teed, mida soovid navigeerimise ajal vältida. Heli Vali hääljuhendamine - Vali või lae alla hääljuhendamine omas keeles. + Vali või laadi alla hääljuhendamine omas keeles. Reaalajas uuendused Saadaolevad kaardid Viimane uuendus: %s @@ -2309,7 +2309,7 @@ Huvipunktide ikoonid Vaheta omavahel lähte- ja sihkoht Vali - Eemalda alla laetud uuendused ja taasta kaardi algversioon + Eemalda allalaaditud uuendused ja taasta kaardi algversioon Lisa ajavahe Tee on kinni Eemalda @@ -2341,10 +2341,10 @@ Pole piisavalt ruumi! \n{3} MB on vajalik ajutiselt ning {1} MB püsivalt. \n(Ainult {2} MB saadaval.) - Kas laeme alla {0} fail(i)\? + Kas laadime alla {0} fail(i)\? \n{3} MB kasutatakse ajutiselt, {1} MB püsivalt. (Kokku {2} MB-st.) - Lae üles OSM märge - Lae üles anonüümselt + Laadi üles OSM märkus + Laadi üles anonüümselt Vali kaardimarker Teised markerid Kuva läbipaistev otsinguriba @@ -2407,14 +2407,14 @@ Eemalda kõik paanid Uuendada kohe kõik kaardid\? Leia minu asukoht - Jäta kaartide alla laadimine vahele + Jäta kaartide allalaadimine vahele Sul ei ole võrguühenduseta kaarte paigaldatud. Võid valida kaardi nimekirjast või kaardid hiljem alla laadida \'Menüü - %1$s\' kaudu. Vali mõni teine piirkond Kaartide otsing… - Las OsmAnd määrab sinu asukoha ja soovitab selle piirkonna jaoks alla laetavaid kaarte. + Las OsmAnd määrab sinu asukoha ja soovitab selle piirkonna jaoks alla laaditavaid kaarte. Asukohta ei leitud Internetiühendus puudub - Vajalik kaartide alla laadimiseks. + Vajalik kaartide allalaadimiseks. Asukoha otsing… Vaba ruum OsmAnd\'i andmekasutus (kaardid, rajafailid, jne.): %1$s. @@ -2423,13 +2423,13 @@ Hangi juhiseid ja avasta uusi kohti ilma internetiühenduseta Miilid/meetrid Hangi %1$s ajaks - Hangi piiramatu kaartide alla laadimine, lisades uuendused iga nädal, päev või isegi tund. + Hangi võimalus kaarte piiramatult alla laadida, lisades uuendusi kord nädalas, päevas või isegi iga tund. Piiramatud kaartide allalaadimised, uuendused ja Vikipeedia laiendus. Hangi see Tellimustasu võetakse valitud ajavahemiku kohaselt. Saad sellest Google Play kaudu igal ajal loobuda. Annetus OSM kogukonnale Osa Sinu annetusest saadetakse OSM\'i kaastöötajatele. Tellimuse hind jääb samaks. - Tellimus võimaldab uuendused igas tunnis, päevas või nädalas ja piiramatu kogu maailma kaartide alla laadimise. + Tellimus võimaldab uuendusi kord tunnis, päevas või nädalas ja piiramatu arvu terve maailma kaartide allalaadimise. Miilid/jalad Austraalia Kabiili @@ -2475,10 +2475,10 @@ Lisa veel… Välimus Peen - Lae üles huvipunkt + Laadi üles huvipunkt Linn või piirkond Lähedalasuvad Vikipeedia artiklid - Lae üles oma OSM märge anonüümselt või kasutades oma OpenStreetMap.org profiili. + Laadi üles oma OSM märge anonüümselt või kasutades oma OpenStreetMap.org profiili. Kasuta kiirteid Lubab kiirteed. Serbia (ladina) @@ -2544,20 +2544,20 @@ Täname OsmAnd tasulise versiooni ostmise eest. Parkimisvalikud Ära saada anonüümset rakenduse kasutamise statistikat - OsmAnd kogub teavet selle kohta, milliseid rakenduse osi te avate. Teie asukohta ei saadeta kunagi, ega ka midagi, mida rakendusse sisestate ega üksikasju piirkondade kohta, mida vaatate, otsite või alla laete. + OsmAnd kogub teavet selle kohta, milliseid rakenduse osi sa avad. Sinu asukohta ei saadeta kunagi, ega ka midagi, mida rakendusse sisestad ega üksikasju piirkondade kohta, mida vaatad, otsid või laadid alla. Ära kuva käivitusteateid Ära kuva rakenduse allahindlusi ega kohalike sündmuste teateid. - GPS-navigeerimine -\n • Valige võrguühenduseta (välismaal viibides rändlustasudeta) või võrgus (kiirem) vahel -\n • Hääljuhised pöörangupõhisel suunamisel juhatavad teid mööda teed (salvestatud ja sünteesitud hääled) -\n • Teekond arvutatakse sellest kõrvale kaldudes alati ümber -\n • Teeäärsed juhised, tänavanimed ja eeldatav saabumisaeg on abiks teel olles -\n • Reisi turvalisemaks muutmiseks lülitatakse päeval/öösel ekraani režiim automaatselt ümber -\n • Näidake kiiruspiiranguid ja saate selle ületamise korral meeldetuletusi -\n • Kaardi suurendustaset kohandatakse vastavalt teie kiirusele -\n • Otsige sihtkohti aadressi, liigi (nt parkimine, restoran, hotell, bensiinijaam, muuseum) või geograafiliste koordinaatide järgi -\n • Toetab teekonna vahepunkte -\n • Salvestage oma andmed või laadige üles GPX rada ja liikuge mööda seda + GPS-navigeerimine +\n • Vali võrguühenduseta (välismaal viibides rändlustasudeta) või võrgus (kiirem) vahel +\n • Hääljuhised pöörangupõhisel suunamisel juhatavad sind mööda teed (salvestatud ja sünteesitud hääled) +\n • Teekond arvutatakse sellest kõrvale kaldudes alati ümber +\n • Teeäärsed juhised, tänavanimed ja eeldatav saabumisaeg on teel olles abiks +\n • Reisi turvalisemaks muutmiseks lülitatakse päeval/öösel ekraani režiim automaatselt ümber +\n • Näitame kiiruspiiranguid ja saad selle ületamise korral meeldetuletusi +\n • Kaardi suurendustaset kohandame vastavalt sinu kiirusele +\n • Otsi sihtkohti aadressi, liigi (nt parkimine, restoran, hotell, bensiinijaam, muuseum) või geograafiliste koordinaatide järgi +\n • Toetab teekonna vahepunkte +\n • Salvesta oma andmed või laadi üles GPX rada ja liigu mööda seda \n Navigeerimine \n • Töötab võrgus (kiire) või võrguühenduseta (välismaal viibides puuduvad rändlustasud) @@ -2568,14 +2568,14 @@ \n • Otsige kohti aadressi, tüübi (nt restoran, hotell, bensiinijaam, muuseum) või geograafiliste koordinaatide järgi \n Analüüsi kaardil - Liitu meie meililistiga rakenduse allahindluste kohta teabe saamiseks ja saate veel 3 kaardi allalaadimist! + Rakenduse allahindluste kohta teabe saamiseks liitu meie postiloendiga ja saad veel 3 kaardi allalaadimist! Nupp ekraani keskkoha teekonna sihtkohaks muutmiseks; ühest varem valitud sihtkohast saab viimane vahesihtkoht. Nupp, mille vajutamine muudab ekraani keskkoha uueks teekonna sihtkohaks, asendades eelnevalt valitud sihtkoha (kui olemas). Nupp ekraani keskkoha esimeseks vahesihtkohaks muutmiseks. Nupp, mis lülitab automaatse kiirusetundliku suumimise sisse või välja. Peatatud Lülita sisse animeeritud oma asukohas kaardi panoraamimine navigeerimise ajal. - Lae alla \'Kõrgusjoonte\' kaart selles piirkonnas kasutamiseks. + Selles piirkonnas kasutamiseks laadi alla „Kõrgusjoonte“ kaart. Osta ja paigalda \'Kõrgusjoonte\' lisa astmestatud vertikaalsete alade kuvamiseks. Nupp, mis lülitab OSM märkmete kaardil kuvamise sisse või välja. Sinu sihtkoht asub eramaal. Luba kasutada erateid sellel teekonnal\? @@ -2585,7 +2585,7 @@ Tänavataseme fotod kõigile. Avasta kohti, tee kaastööd, jäädvusta maailma. Paranda foto katvust Mapillary abil Piltide filtreerimine lähetaja, kuupäeva või liigi alusel. Aktiivne ainult lähivaate suurenduse korral. - Ajakohaste andmete nägemiseks lae paanid uuesti. + Ajakohaste andmete nägemiseks laadi paanid uuesti. Salvesta salvestatud rajad kuude kaupa kaustades Salvesta salvestatud rajad salvsetamise kuu kohastes alamkataloogides (näiteks 2018-01). Keskmine @@ -2680,10 +2680,10 @@ See lisa pakub nii kõrgusjoonte ülekatte kui ka (reljeefse) künkavarjutuse kihi, mida kuvatakse OsmAnd standardkaartidel. Seda funktsionaalsust hindavad sportlased, matkajad, rändajad ja kõik teised, kes on huvitatud maastiku reljeefstruktuurist. \n \nGlobaalsed andmed (vahemikus 70 ° põhja ja 70 ° lõuna) põhinevad mõõtmistel, mille on teinud SRTM (süstiku radari topograafiamissioon) ja ASTER (täiustatud kosmose termilise kiirguse ja peegelduse radiomeeter), NASA maavaatlussatelliitide süsteemi lipulaeva Terra pardal olev pildistamisinstrument. ASTER on NASA, Jaapani majandus-, kaubandus- ja tööstusministeeriumi (METI) ja Jaapani kosmosesüsteemide (J-kosmosesüsteemid) koostöö. - See laiendus pakub nii kõrgusjoonte ülekatte kui ka (reljeefse) künkavarjutuse kihi, mida kuvatakse OsmAnd standardkaartidel. Seda funktsionaalsust hindavad sportlased, matkajad, rändajad ja kõik teised, kes on huvitatud maastiku reljeefstruktuurist. (Pange tähele, et kõrgusjoonte ja/või reljeefi andmed on eraldi, pärast laienduse aktiveerimist on saadaval täiendavald alla laetavad failid.) + See laiendus pakub nii kõrgusjoonte ülekatte kui ka (reljeefse) künkavarjutuse kihi, mida kuvatakse OsmAnd standardkaartidel. Seda funktsionaalsust hindavad sportlased, matkajad, rändajad ja kõik teised, kes on huvitatud maastiku reljeefstruktuurist. (Pange tähele, et kõrgusjoonte ja/või reljeefi andmed on eraldi, pärast laienduse aktiveerimist on saadaval täiendavad allalaaditavad failid.) \n \nGlobaalsed andmed (vahemikus 70 ° põhja ja 70 ° lõuna) põhinevad mõõtmistel, mille on teinud SRTM (süstiku radari topograafiamissioon) ja ASTER (täiustatud kosmose termilise kiirguse ja peegelduse radiomeeter), NASA maavaatlussatelliitide süsteemi lipulaeva Terra pardal olev pildistamisinstrument. ASTER on NASA, Jaapani majandus-, kaubandus- ja tööstusministeeriumi (METI) ja Jaapani kosmosesüsteemide (J-kosmosesüsteemid) koostöö. - Selle vaate aktiveerimine muudab OsmAndi kaardistiili \'Reisivaateks\'. See on reisijatele ja kutselistele autojuhtidele mõeldud eriline kõrglahutusega vaade. + Selle vaate aktiveerimine muudab OsmAndi kaardistiili „Reisivaateks“. See on reisijatele ja kutselistele autojuhtidele mõeldud eriline kõrglahutusega vaade. \n \nSee vaade pakub igal kaardi suurendamisel maksimaalse hulga kaardiandmetes saada olevaid reisiandmeid (eriti teed, rajad ja orientatsioonimärgid). \n @@ -2693,7 +2693,7 @@ \n \nSpetsiaalset kaardi allalaadimist pole vaja, vaade on loodud meie tavalistest kaartidest. \n -\nSelle vaate saab tagasi lülitada, aktiveerides selle siin uuesti või muutes vastavalt vajadusele jaotises \'Kaardi seadistamine\' \'Kaardistiil\'. +\nSelle vaate saad tagasi lülitada, aktiveerides selle siin uuesti või muutes vastavalt vajadusele jaotises „Kaardi seadistamine“ „Kaardistiil“. See lisa rikastab OsmAnd kaardi ja navigatsioonirakendust, et toota ka merekaarte paadisõiduks, purjetamiseks ja muud tüüpi veespordi jaoks. \n \nSpetsiaalne OsmAnd kaardi lisamoodul pakub kõiki merenavigatsioonimärke ja kaardisümboleid nii sisemaal kui ka rannikul navigeerimiseks. Iga navigatsioonimärgi kirjeldus sisaldab nende identifitseerimiseks vajalikke üksikasju ja tähendust (kategooria, kuju, värv, järjestus, viide jne). @@ -2732,7 +2732,7 @@ \n * Pikk puudutus punktil kirjelduse vaatamiseks ja lisamiseks. \n * Puuduta mõõtmisvidinat täpsemate tegevuste nägemiseks. Kompassi lugemiseks kasuta orientatsioonianduri asemel magnetilist andurit. - WiFi ühendus puudub. Kas kasutada allalaadimiseks praegust internetiühendust\? + WiFi ühendus puudub. Kas kasutame allalaadimiseks praegust internetiühendust\? Kuvab taustajälgimise ja navigeerimise sisselülitamise sätteid GPS-seadet perioodiliselt äratades (välja lülitatud ekraaniga). Oled kindel, et soovid sihtkoha (ja vahepealsed sihtkohad) tühjendada\? Sünkrooni salvestatud radasid ja heli- või videomärkmeid oma Dropboxi kontole. @@ -2756,45 +2756,45 @@ Optimeeri kaarti Kuva alates suurendustasemest (vajab kõrgusandmeid): Täpsema teabe saamiseks puuduta mõnda olemasolevat üksust, mitteaktiveerimiseks või kustutamiseks puuduta pikalt. Seadme praegused andmed (%1$s vaba): - Täname OsmAnd kasutamise eest. Lae piirkondlikud andmed võrguühenduseta kasutamiseks alla menüüst \'Seaded\' → \'Halda kaardifaile\', et vaadata kaarte, otsida aadresse, leida huvipunkte, ühistransporti ja palju muud. + Täname OsmAnd kasutamise eest. Laadi piirkondlikud andmed võrguühenduseta kasutamiseks alla menüüst „Seaded“ → „Halda kaardifaile“, et vaadata kaarte, otsida aadresse, leida huvipunkte, ühistransporti ja palju muud. Veebis ja vahemällu salvestatud paanikaardid - Lae alla ja halda oma seadmesse salvestatud võrguühenduseta kaardifaile. + Laadi alla ja halda oma seadmesse salvestatud võrguühenduseta kaardifaile. Veebipõhised ja paanikaardid Lisad aktiveerivad täpsemad sätted ja täiendavad funktsioonid. Vektorkaardid kuvatakse tõenäoliselt kiiremini. Mõne seadme puhul ei pruugi see hästi töötada. Vali hääl ja proovi esitades teateid: Sisseehitatud renderdamine Testi hääljuhiseid - Lae selle asukoha jaoks võrguühenduseta vektorkaart alla menüüst \'Seaded\' (\'Halda kaardifaile\') või lülitu ümber \'Veebikaardid\' lisale. + Laadi selle asukoha jaoks võrguühenduseta vektorkaart alla menüüst „Seaded“ („Halda kaardifaile“) või lülitu ümber „Veebikaardid“ lisamoodulile. GPX failide üleslaadimiseks täpsusta palun oma OSM kasutajanimi ja salasõna. Muuda kaardi suurendustaset juhtkuuli horisontaalse liigutusega. Kasuta juhtkuuli suurendustaseme juhtimiseks Valitud ala kuvamiseks pole piisavalt protsessimälu - Rakendusesisesed huvipunktide muudatused ei mõjuta alla laetud kaardifaile, muudatused salvestatakse failina seadmesse. + Rakendusesisesed huvipunktide muudatused ei mõjuta allalaaditud kaardifaile, muudatused salvestatakse failina seadmesse. Kuva ja halda seadme andmebaasis märgitud OSM huvipunkte ja märkmeid. Täpsusta veebiaadress parameetrite süntaksiga: lat={0}, lon={1}, timestamp={2}, hdop={3}, altitude={4}, speed={5}, bearing={6}. Logi rada kasutades GPX vidinat või \'Reisi salvestamise\' sätteid. Varem eksporditud lemmikuid sisaldav fail on juba olemas. Asendada see\? - Pole piisavalt vaba ruumi %1$s MB alla laadimiseks (vaba: %2$s). + Pole piisavalt vaba ruumi %1$s MB allalaadimiseks (vaba: %2$s). See seade ei toeta süsteemi teeki. Süsteemi teegi lähtestamine… Vektorite renderdamise valikud Leiti mitu seotud huvipunktide kategooriat. - Huvipunktide otsimiseks lae alla võrguühenduseta andmed. + Huvipunktide otsimiseks laadi alla võrguühenduseta andmed. Huvipunktide andmefail \'%1$s\' on üleliigne ja selle võib kustutada. Kohalikku faili huvipunktide muudatuste säilitamiseks ei leitud ega saanud ka luua. - Uute kaardifailide kasutamiseks lae alla rakenduse uus versioon. - Lae GPX failid üles OSM kogukonda, parandades seeläbi kaarte. + Uute kaardifailide kasutamiseks laadi alla rakenduse uus versioon. + Laadi GPX failid üles OSM kogukonda, parandades seeläbi kaarte. Kuva mõningaid vektorkaardi detaile (teed jne) juba madalamatel suurendustasemetel. Oled kindel, et soovid kustutada %1$d lemmikut ja %2$d lemmikute gruppi\? - Lae alla maailma põhikaart, et saada ülevaade kogu maailmast ka madalamatel suurendustasemetel. - Lae alla (\'võrguühenduseta\') andmed kaartide võrguühenduseta kasutamiseks. + Saamaks ülevaade kogu maailmast ka madalamatel suurendustasemetel, laadi alla maailma põhikaart. + Laadi alla andmed kaartide võrguühenduseta kasutamiseks. Puuduvad üksused %1$s Paigaldatud Android TTS (kõnesünteesi) mootor ei toeta valitud keelt, selle asemel kasutatakse selle eelhäälestatud TTS keelt. Kas otsida turult teist TTS-mootorit\? Selle asukoha jaoks on olemas võrguühenduseta vektorkaart. \n \nKasutamiseks aktiveeri menüü \'Menüü\' → \'Seadista kaart\' → \'Kaardiallikas…\' → \'Võrguühenduseta vektorkaardid\'. - Rakendus ei suuda alla laadida kaardikihti %1$s, uuesti paigaldamine võib aidata. + Rakendus ei suuda alla laadida kaardikihti %1$s, selle uuesti paigaldamine võib aidata. Tegevust ei saa sooritada ilma internetiühenduseta. Kasuta rasterkaarte kõigeks seda taset ületavaks. Vähim vektori suurendustase @@ -2806,16 +2806,16 @@ \n \nNavigeerimine lülitati ajutiselt ümber CloudMade võrguteenusele. Uues rakenduses toetatakse kõiki vanas paigaldatud rakenduses olevaid võrguühenduseta andmeid, kuid lemmikpunktid tuleb vanast rakendusest eksportida ja seejärel uues importida. - Hääljuhiseid ei ole saadaval, mine menüüsse \'Seaded\' → \'Navigeerimise seaded\', vali profiil → \'Hääljuhised\' ja vali või lae alla hääljuhiste pakett. + Hääljuhiseid ei ole saadaval, mine menüüsse „Seaded“ → „Navigeerimise seaded“, vali profiil → „Hääljuhised“ ja vali või laadi alla hääljuhiste pakett. Reguleeri öö- ja päevarežiimi vahetamist. Luba, et arvutada kiireim teekond või keela kütusesäästliku teekonna jaoks. - Suurendustasemel {0} lae alla {1} paani ({2} MB) - Maksimaalne eellaetav suurendustase - Seda kaarti ei saa alla laadida + Suurendustasemel {0} laadi alla {1} paani ({2} MB) + Maksimaalne eellaaditav suurendustase + Seda kaarti ei õnnestunud alla laadida Radade kaustast ei leitud ühtegi GPX faili Salvestada andmed GPX failina või importida teekonnapunktid \'Lemmikud\' hulka\? Lemmikuid sisaldavat GPX faili ei leitud asukohast {0} - Mälukaardilt ei leitud ühtegi alla laetud kaarti. + Mälukaardilt ei leitud ühtegi allalaaditud kaarti. Aitäh Yandexile liiklusinfo eest. Kasuta juhtpalli kaardi liigutamiseks. Kasuta juhtrullikut @@ -2844,11 +2844,11 @@ Mälukaardile pole juurdepääsu. \nSa ei näe kaarte ega leia asju. Mälukaart on kirjutuskaitstud. -\nNüüd on võimalik vaadata ainult eellaetud kaarti, mitte uusi alasid alla laadida. +\nNüüd on võimalik vaadata ainult eellaaditud kaarti, mitte uusi alasid alla laadida. Faili lahti pakkimine… Suundu Hiljem - Lae alla piirkondi + Laadi alla piirkondi Signaali ootel… Otsi praeguse kaardi keskkoha lähedusest Otsi lähedusest @@ -2872,7 +2872,7 @@ Kuva ühistranspordi peatused kaardil. Kuva ühistranspordi peatused OsmAnd navigeerimisrakendus - Huvipunktide andmed uuendatud (laeti {0}) + Huvipunktide andmed uuendatud (laaditi {0}) Kohalike huvipunktide loetelu uuendamine ebaõnnestus. Kaardi suurendamine võimaldab huvipunkte uuendada Linn: {0} @@ -2880,17 +2880,17 @@ Ristmik: {0} x {1} kohas {2} Lemmik Andmete üleslaadimine… - Üleslaadimine… + Laadin üles… Midagi ei leitud Otsimine… Aadressi otsimine… Võrguühenduseta otsing Vahemaa kokku %1$s, sõiduaeg %2$d h %3$d min. Mälukaardil olevale andmesalvestuskaustale pole juurdepääsu! - Laadida alla {0} - {1} \? + Kas laadime alla {0} - {1} \? Võrguühenduseta andmed {0} on juba olemas ({1}). Värskendada see ({2})\? Aadress - Saadaolevate piirkondade loetelu allalaadimine… + Laadin alla saadaolevate piirkondade loetelu… Piirkondade loendit ei õnnestunud hankida aadressilt https://osmand.net. Lemmikpunkti muudeti Lemmikpunkte pole @@ -2901,28 +2901,28 @@ Muudatuste komplekti avamine… Muudatuste komplekti sulgemine… Sõlme kinnitamine… - HP laadimine… + Laadin HP… Autoriseerimine ebaõnnestus ebaõnnestus Kohalike/ingliskeelsete nimede teisendamine… - Postiindeksite laadimine… - Tänavate laadimine… - Linnade laadimine… + Laadin postiindekseid… + Laadin tänavaid… + Laadin linnasid… HP GPX faili salvestamine ebaõnnestus. Oled kohale jõudnud. Vigased koordinaadid Tagasi kaardile - Andmete laadimine… + Laadin andmeid… Kohalike andmete lugemine… OsmAnd jooksis viimasel kasutamisel kokku. Logifail asub {0}. Palun teata probleemist ja edasta logifail. GPX faili salvestamine… Valmis Määra OSM kaastööks vajalikud OpenStreetMap.org (OSM) sätted. - Määra keel, lae alla või lae andmed uuesti. + Määra keel, laadi alla või laadi andmed uuesti. Andmed Lisaseadistused - Lae paan uuesti + Laadi paan uuesti Sihtmärk Valige kohalike ja ingliskeelsete nimede vahel. Kasuta kaartidel ingliskeelseid nimesid @@ -2941,7 +2941,7 @@ Kasuta internetti Kuva oma asukoht Kuva kaardil GPS koordinaadid - Lae alla puuduvad kaardipaanid + Laadi alla puuduvad kaardipaanid Navigeerimisrakendus Otsi Otsi @@ -2984,7 +2984,7 @@ Tegevus {0} lõpetatud. Tegevust {0} ei saanud sooritada. I/O tõrge tegevuse {0} sooritamisel. - Sõlme teavet ei laetud + Sõlme teavet ei laaditud Avatud Kommentaar Huvipunkti muutmine @@ -3018,7 +3018,7 @@ Palun anna meile teada kõigist ettepanekutest. Ei saanud üles laadida Kustuta muudatus - Üles laetud {0}/{1} + Üles laaditud {0}/{1} Proovi uuesti Viga: {0} Seadista armatuurlaud @@ -3059,7 +3059,7 @@ Allalaadimised Ainult maanteede kaarti pole vaja, sest sul on tavaline (täielik) kaart. Kas soovid selle ikkagi alla laadida\? %.1f MB - Tasuta allalaadimised ära kasutatud + Tasuta allalaadimised on ära kasutatud Kuvab järelejäänud tasuta allalaadimiste arvu. Vali, kuhu soovid kaarte ja muid andmefaile salvestada. Sisesta riigi nimi @@ -3091,7 +3091,7 @@ Kasuta armatuurlauda Kasuta menüüd %1$.1f / %2$.1f MB - Lae alla ainult WiFi kaudu + Laadi alla ainult WiFi kaudu OsmAndil puudub mälukaardi kasutamise luba Viimane kaardimuudatus: %s Iga tund @@ -3204,12 +3204,12 @@ \n • Vikipeedia sinu eelistatud keeles võib sulle linnaekskursiooni ajal palju öelda \n • Ühistranspordipeatused (buss, tramm, rong) koos liinide nimedega aitavad uues linnas navigeerida \n • Jalakäijate režiimis GPS navigeerimine loob sulle marsruudi kõnniteede abil -\n • Lae üles GPX marsruut ja järgi seda või registreeri ja jaga enda oma +\n • Laadi üles GPX marsruut ja järgi seda või salvesta ja jaga enda oma \n Panusta OSM kvaliteeti -\n • Andmevigadest teatamine -\n • Lae GPX rajad OSM keskkonda üles otse rakendusest -\n • Lisa huvipunkte ja lae need kohe üles OSM keskkonda (või hiljem internetühenduse tekkides) +\n • Teata andmevigadest +\n • Laadi GPX rajad OSM keskkonda üles otse rakendusest +\n • Lisa huvipunkte ja laadi need kohe üles OSM keskkonda (või hiljem internetühenduse tekkides) \n OsmAnd on aktiivselt arendatud avatud lähtekoodiga tarkvara. Kõik saavad rakendusse panustada, teatades vigadest, parandades tõlkeid või luues uut funktsioonaalsust. Lisaks kasutab projekt rahalisele toetuset programmerimiseks ja uute funktsioonide testimiseks. \n Ligikaudne kaardi leviala ja kvaliteet: @@ -3261,11 +3261,11 @@ \n • Valikuline kiiruse ja kõrguse kuvamine \n • Kõrgusjoonte ja künkavarjutuse kuvamine (täiendava lisa abil) Tee otse OSM kaastööd -\n • Andmevigadest teatamine -\n • Lae GPX radu OSM keskkonda otse rakendusest -\n • Lisa huvipunkte ja lae need kohe üles OSM keskkonda (või hiljem internetiühenduse tekkimisel) -\n • Valikuline reisi salvestamine ka taustrežiimis (kui seade on puhkeolekus) -\n OsmAnd on aktiivselt arendatud avatud lähtekoodiga tarkvara. Kõik saavad rakendusse panustada, teatades vigadest, parandades tõlkeid või kodeerides uusi funktsioone. Lisaks toetub projekt rahalisele toetusele koodide kodeerimiseks ja uute funktsioonide testimiseks. +\n • Teata andmevigadest +\n • Laadi GPX radu OSM keskkonda otse rakendusest +\n • Lisa huvipunkte ja laadi need kohe üles OSM keskkonda (või hiljem internetiühenduse tekkimisel) +\n • Kui soovid, siis salvesta teekonda ka taustrežiimis (kui seade on puhkeolekus) +\n OsmAnd on aktiivselt arendatud avatud lähtekoodiga tarkvara. Kõik saavad rakendusse panustada, teatades vigadest, parandades tõlkeid või kodeerides uusi funktsioone. Lisaks toetub projekt rahalisele toetusele nii programmeerimiseks kui ka uue funktsionaalsuse testimiseks. \n Ligikaudne kaardi leviala ja kvaliteet: \n • Lääne-Euroopa: **** @@ -3278,7 +3278,7 @@ \n • Lähis-Ida: ** \n • Aafrika: ** \n • Antarktika: * -\n Enamike riikide andmed üle maailma on saadaval allalaadimiseks +\n Enamike riikide andmed üle maailma on allalaadimiseks saadaval \n Afganistanist Zimbabweni, Austraaliast Ameerika Ühendriikideni. Argentina, Brasiilia, Kanada, Prantsusmaa, Saksamaa, Mehhiko, Suurbritannia, Hispaania, … \n Nimi sisaldab liiga palju suurtähti. Jätkata\? @@ -3302,8 +3302,8 @@ Nimetu asukoht Tunnel ees Tunnelid - Lae alla Vikipeedia artiklid %1$s kohta ilma võrguühenduseta lugemiseks. - Lae alla Vikipeedia andmed + Laadi alla Vikipeedia artiklid %1$s kohta ilma võrguühenduseta lugemiseks. + Laadi alla Vikipeedia andmed Vaata artiklit veebilehitsejas. see piirkond Vastava viki artikli otsing @@ -3358,9 +3358,9 @@ Teekonna ümberarvutamine Teata Kasutajanimi ja salasõna - Need seaded on üldised ja rakenduvad kõikidele profiilidele + Need pistikprogrammi seaded on üldised ja rakenduvad kõikidele profiilidele OSM\'i andmete muutmine - Kõiki üleslaadimata muudatusi või OSM vigu saad vaadata menüüs %1$s. Üleslaaditud punkte OsmAnd ei kuva. + Vaata kõiki üleslaadimata muudatusi või OSM vigu saad menüüs %1$s. Juba üleslaaditud muudatusi OsmAnd ei kuva. OSM Ikooni kuvatakse vaid navigeerimise või liikumise ajal. Peatumisel näidatav ikoon. @@ -3420,7 +3420,7 @@ Kõrgusreljeefi kaart tumedate varjunditega nõlvade, tippude ja tasandike kirjeldamiseks. Raja nõlva kallet visualiseeritakse värvidega. Määra vähima ja suurima suurenduse kaardikihi kuvamisel. - Kõrgusreljeefi kuvamiseks on vaja täiendavaid kaarte. + Mäevarjude kaardil vaatamiseks on vaja täiendavaid kaarte. Nõlvade kuvamiseks on vaja täiendavaid kaarte. Nõlvadest saab rohkem lugeda %1$s-s. Läbipaistvus @@ -3556,8 +3556,8 @@ Nutiseadme helitugevuse nuppudega kontrollida saad kaardi suumi taset. Helitugevuse nupud toimivad suumina Palun sisestage punkti nimi - Lae alla Vikipeedia kaardid - Hangi huviväärsuste kohta teavet Vikipeediast. See on sinu võrguühenduseta reisiraamat - lihtsalt võta kasutusele Vikipeedia lisaprogramm ja loe artikleid enda ümber asuvate objektide kohta. + Laadi alla Vikipeedia kaardid + Hankige teavet huvipunktide kohta Wikipediast, taskukohasest võrguühenduseta juhendist, mis sisaldab artikleid kohtade ja sihtkohtade kohta. Kuna valitud grupp on peidetud, siis lisatud punkt ei ole kaardil nähtav. Vajadusel leiad ta „%s“ alt. Kartauto Parkimiskohad @@ -3574,7 +3574,7 @@ Toitenupp Eemalda Täiendavad kaardid - Vali kuidas sa soovid allalaetud paane salvestada. + Vali kuidas sa soovid allalaaditud paane salvestada. OsmAnd ja Mapillary Kiirtegevus Raadiuse joonlaud @@ -3646,14 +3646,14 @@ Navigeerimisprofiil Vali rajafail, millele lisame uue segmendi. Tänavataseme vaated - Kas sa oled kindel et soovid sulgeda teekonna planeerija ilma seda salvestamata\? + Kas oled kindel, et soovid loobuda kõikidest tehtud muudatustest planeeritud teekonnal\? Vastupidise suuna korral Muuda järjekorda või peida kirjed %1$s\'st. Need kirjed on menüüs peidetuna, kuid tehtud valikud või lisaprogrammid on jätkuvalt kasutatavad. Sa võid kirjeid liigutada vaid selle kategooria piires. Kirjed Taasta kirjete vaikimisi järjekord - Puhverdatud paanid laetakse uuesti alla peale nimetatud minutite möödumist. Kui sa ei soovi uuendamist, siis jäta väli tühjaks. + Puhverdatud paanid laaaditakse uuesti alla peale nimetatud minutite möödumist. Kui sa ei soovi uuendamist, siis jäta väli tühjaks. \n \nÜks päev on 1440 minutit. \nÜks nädal on 10080 minutit. @@ -3695,7 +3695,7 @@ Salvestatakse ainult üldine marsruut, teekonnapunktid kustutatakse. Faili nimi %s rajafaili valitud - Peatab raja logimise rakenduse jõuga sulgemise korral (hiljutiste rakenduste kaudu). (OsmAndi taustnäit kaob Androidi teavitusribalt.) + Raja logimine peatub, kui rakendus tapetakse (viimaste rakenduste kaudu). (OsmAnd-i tausttähis kaob Androidi teateribalt.) Määra üldisel raja salvestamisel kasutatav logimisvälp (lülitatakse sisse kaardil asuvast teekonna salvestamise vidinast). Süsteemi vaikeseadistused Kõik järgnevad segmendid @@ -3721,7 +3721,7 @@ \nValides %1$s sa saad kiiruskaamerate-kohaseid teateid ja hoiatusi. \n \nValides %2$s kustutatakse kõik kiiruskaameratega seotud andmed, nagu hoiatused, teated ja huvipunktid seniks, kuni sa OsmAnd\'ile ei tee täiesti uut paigaldust. - Praegune teekonna vahepunkt saab kustutatud. Kui ta osutub sihtkohaks, siis navigatsioon lõppeb. + Kustutab järgmise marsruudi sihtkoha. Kui see on lõppsihtkoht, siis navigeerimine peatub. Muuda teekonna tüüpi pärast Muuda teekonna tüüpi enne Kärbi pärast @@ -3767,12 +3767,12 @@ Väldi jalgteid Väldi jalgteid Täname „Kõrgusjoonte“ ostu eest - Ostukinnituse saabumisel arveldame tellimuse eest sinu AppGallery konto alusel. + Teie AppGallery konto eest võetakse tasu ostu kinnitamisel. \n -\nKui sa ei tühista tellimust enne uue perioodi algust, siis tellimus pikeneb automaatselt ning arveldame järgmise ajavahemiku eest (kuu/kvartal/aasta) selle alguses. +\nTellimus pikeneb automaatselt, kui seda ei tühistata enne uuendamise kuupäeva. Teie kontolt võetakse pikendamisperioodi (kuu / kolm kuud / aasta) eest tasu ainult pikendamise kuupäeval. \n -\nTellimust saad hallata ja tühistada AppGallery seadistustest. - Arveldame tellimuse eest valitud ajavahemiku alusel. Seda saad sa vabalt valitud ajal tühistada AppGallery\'s. +\nTellimusi saate hallata ja tühistada oma AppGallery seadetes. + Tellimuse eest võetakse tasu valitud perioodi kohta. Tühistage see igal ajal oma AppGallery\'s. Keeruka teekonna koostamine Väljalogitud Arendus @@ -3798,11 +3798,11 @@ OsmAnd kasutab MGRS-vormingut, mis on sarnane NATO UTM-vormingule. MGRS MGRS - Graafiku joonistamiseks peaksid lisama vähemalt kaks punkti + Lisa vähemalt kaks punkti OsmAnd Live tellimus on ootel OsmAnd Live tellimus on peatatud OsmAnd Live tellimus on aegunud - Sinu tellimusega on üks pisikene segadus. Selleks et Google Play seadistuses makseviisi parandada, palun klõpsi seda nuppu. + Tellimuse parandamiseks puudutage nuppu Google Play makseviisi seadistamiseks. Halda tellimusi Logi sisse OpenStreetMapi kasutajakontoga Kasutajanimi @@ -3818,7 +3818,7 @@ Lisa foto Registreeru \nOpenPlaceReviews.org saidis - Fotode allikaks on OpenPlaceReviews.org veebisait. Oma fotode üleslaadimiseks peaksid liituma selle veebisaidi kasutajaks. + Fotode allikaks on avatud andmetel põhinev OpenPlaceReviews.org veebisait. Oma fotode üleslaadimiseks peaksid liituma selle veebisaidi kasutajaks. Loo uus kasutajakonto Mul juba on kasutajakonto olemas Otsinguajalugu @@ -3838,7 +3838,7 @@ Lisa OpenPlaceReviews saiti Tavasaidi openstreetmap.org asemel kasuta OSM märkuste, huvipunktide või GPX-failide üleslaadimise katsetamiseks arendussaiti dev.openstreetmap.org. Kasuta dev.openstreetmap.org saiti - OsmAnd kuvam fotosid mitmetest allikatest: + OsmAnd kuvab fotosid mitmetest allikatest: \nOpenPlaceReviews - huvipunkti fotod; \nMapillary - tänavavaated; \nWikimedia või veeb - OpenStreetMap\'i andmetes kirjeldatud fotod. @@ -3852,4 +3852,100 @@ Liida lõigud Kasutajaprofiil OsmAnd profiil + Vaikimisi keelatud: kui esiplaanil töötab OsmAnd, ei aegu ekraan läbi. +\n +\nKui see on lubatud, kasutab OsmAnd süsteemi ajalõpu sätet. + Kasutage süsteemi ekraani ajalõppu + "• Profiilid: nüüd saate muuta järjestust, määrata kaardi ikooni, muuta kõiki põhiprofiilide sätteid ja taastada need vaikeseadetesse +\n +\n • Navigatsioonis on lisatud väljumisnumber +\n +\n • Ümber töötatud pistikprogrammi seaded +\n +\n • Kõigile profiilidele kiireks juurdepääsuks kuvatakse ümbertöödeldud seadete ekraan +\n +\n • Lisatud võimalus seadete kopeerimiseks teiselt profiililt +\n +\n • Lisatud võime muuta järjestust või peita HP kategooriaid otsingus +\n +\n • POI ikoonid kaardil õigesti joondatud +\n +\n • Kaardi seadistamiseks on lisatud päikeseloojangu / päikesetõusu andmed +\n +\n • Lisatud kaardil kodu / töökoha ikoonid +\n +\n • Lisatud seadetes mitmerealise kirjelduse tugi +\n +\n • Lisas Jaapani kaardile õige transliteratsiooni +\n +\n • Lisatud Antarktika kaart +\n +\n" + Teekond arvutatakse ümber, kui vahemaa teekonnani on suurem kui määratud parameeter + See pistikprogramm on eraldi rakendus, peate selle eraldi eemaldama, kui te ei kavatse seda enam kasutada. +\n +\nPistikprogramm jääb seadmesse pärast OsmAnd eemaldamist. + Pistikprogramm välja lülitatud + Kohandage üksuste hulka jaotistes \"Sahtel\", \"Kaardi seadistamine\" ja \"Kontekstimenüü\". +\n +\nKõigi nende juhtnuppude peitmiseks lülitage kasutamata pistikprogrammid välja. %1$s. + Sahtli osad, kontekstimenüü + Sahtel + Ühendage erinevate kategooriate HP-de tüübid. Kõigi valimiseks puudutage lülitit, kategooria valimiseks puudutage vasakut serva. + "• Uued võrguühenduseta nõlvakaardid +\n +\n • Lemmikute ja GPX-teekonnapunktide täielik kohandamine - kohandatud värvid, ikoonid, kujundid +\n +\n • Kohandatud üksuste järjekorda jaotistes \"Kontekstimenüü\", \"Kaardi seadistamine\" ja \"Sahtel\" +\n +\n • Vikipeedia eraldi kihina jaotises Kaardi seadistamine valige ainult vajalikud keeled +\n +\n • Loodud oma HP-de filter / kaardid täieliku paindlikkusega +\n +\n • Lisatud valikud kohandatud profiilide sätete taastamiseks +\n +\n • Täielikud GPX-marsruudid navigeerimisest toetavad liiklusradasid ja täitke pööramisjuhised +\n +\n • Parandatud tahvelarvutite kasutajaliidese suurused +\n +\n • Parandatud vead RTL-iga +\n +\n" + Lülitab ekraani välja vastavalt süsteemi ekraani aegumisele. + Kasuta süsteemi ekraani ajalõppu + Valige ekraani äratussuvandid (veenduge, et seadme lukustamisel oleks esiplaanil OsmAnd): + Keelatud. Nõuab jaotises „Aegumise aeg pärast ärkamist” valikut „Hoia ekraan sisse lülitatud”. + Valige pärast ärkamist ekraani ajalõpp. (\"%1$s\" ei kasuta ajalõppu.) + Mõjutab ekraani, kui seda kasutatakse kaardi või ülekatte / aluskattena. +\n +\n%1$s: kaart on piiratud valitud suurendustaseme vahemikuga. +\n +\n%2$s on tasemed, kus algsed paanid on nähtavad, suurem või suurem vähendamine toimub väljaspool neid väärtusi. + Alguse ja lõpu ikoonid + Kahefaasiline marsruut autoga navigeerimiseks. + Saksa (harilik) + Kohaliku ühistranspordi arendamine + Lülita ümber Java (turvaline) ühistranspordi teekonna arvutamisele + Graafik + %1$s andmed on saadaval ainult teedel. Graafikute nägemiseks arvutage marsruut, kasutades \"Punktidevaheline marsruut\". + Palun oota. +\nGraafik on saadaval peale teekonna ümberarvutust. + Vahe + Logi OpenStreetMap-i sisse + Logi sisse OpenStreetMap.org keskkonda + Sisesta komadega eraldatud sildid. + \"Avalik\" tähendab, et jälge kuvatakse avalikult teie GPS-i jälgedes ja avalikes GPS-i jälgede loendites ning avalikus jäljendite loendis koos ajatemplitega toores vormis. API kaudu edastatud andmed ei viita teie jälgimislehele. Jälgimispunkti ajatemplid pole avaliku GPS-i API kaudu saadaval ja jälgimispunktid pole kronoloogiliselt järjestatud. + „Privaatne” tähendab, et jälge ei kuvata üheski avalikus loendis, kuid selle sünkroniseerimata järjekorras olevad jälgimispunktid on avaliku GPS-i API kaudu saadaval ilma ajatempliteta. + „Tuvastatav” tähendab, et jälg kuvatakse avalikult teie GPS-i jälgedes ja avalikes GPS-i jälgede loendites, st teised kasutajad saavad toore jälje alla laadida ja seostada teie kasutajanimega. Avalikud ajatempliga jäljepunkti andmed GPS-i API-st, mida teenindatakse rajapunktide API kaudu, viitavad teie algsele jälgimislehele. + \"Jälgitav\" tähendab, et jälg ei ilmu ühteski avalikus loetelus, kuid töödeldud teekonnapunktid koos ajatemplitega sellest (mida ei saa seostada teiega otse) ilmuvad avaliku GPS API allalaadimiste kaudu. + • Lisatud võimalus eksportida ja importida kõiki andmeid, sealhulgas seadeid, ressursse, oma kohti +\n +\n• Marsruudi planeerimine: graafikud rööbastee segmentide marsruudiga ja lisaks võime luua ja muuta mitut raja segmenti +\n +\n• Lisatud OAuth autentimise meetod OpenStreetMap, täiustatud UI OSM dialooge +\n +\n• Kohandatud lemmikvärvide ja asukohapunktide jälgimise toetus +\n +\n + Pööra kõik punktid ümber \ No newline at end of file diff --git a/OsmAnd/res/values-fa/strings.xml b/OsmAnd/res/values-fa/strings.xml index e5a0251d4b..fe55b9e35f 100644 --- a/OsmAnd/res/values-fa/strings.xml +++ b/OsmAnd/res/values-fa/strings.xml @@ -2063,7 +2063,7 @@ بازگشایی دریافت محاسبهٔ مجدد هوشمند - فقط بخش ابتدایی مسیر را مجدداً محاسبه می‌کند. برای سفرهای طولانی مفید است. + فقط بخش ابتدایی مسیر را مجدداً محاسبه می‌کند. سودمند برای سفرهای طولانی. آیا از OsmAnd راضی هستید؟ دیدگاه و بازخورد شما ارزشمند است. به این برنامه امتیاز بدهید @@ -3481,7 +3481,7 @@ اجازه می‌دهد موقعیت کنونی با استفاده از ضبط سفر هم‌رسانی شود. ردهای ضبط‌شدهٔ شما در %1$s یا در پوشهٔ OsmAnd قرار دارند. یادداشت‌های OSMای شما در %1$s قرار دارند. - می‌توانید همهٔ ویرایش‌ها یا یادداشت‌های OSMای خود را که آپلود نکرده‌اید در %1$s ببینید. نقاط آپلودشده را دیگر در OsmAnd نمی‌بینید. + همهٔ ویرایش‌ها یا یادداشت‌های OSMای آپلودنشدهٔ خود را در %1$s ببینید. تغییرات آپلودشده را دیگر نمی‌بینید. لاگ‌های جزئی برنامه را بررسی و هم‌رسانی کنید استفاده از برنامهٔ سیستم صدای شاتر دوربین @@ -3925,7 +3925,7 @@ %s فایل رد انتخاب شده است ضبط هنگامی که برنامه را از طریق برنامه‌های اخیر ببندید ضبط رد به‌طور موقت می‌ایستد. (نشانگر اجرای پس‌زمینه از نوار اعلان حذف می‌شود.) - بازهٔ زمانی برای ضبط رد را انتخاب کنید (که از طریق ابزار ضبط سفر روی نقشه فعال می‌شود). + بازهٔ زمانی برای ضبط رد را انتخاب کنید (که از طریق ابزارک ضبط سفر روی نقشه روشن می‌شود). نگه‌داشتن ضبط سفر ازسرگیری ضبط سفر اسکیت این‌لاین @@ -3933,7 +3933,7 @@ اسکوتر موتوری ویلچر رو به جلو فاصله آستانه - نمادهای ابتدا/انتها + نمادهای ابتدا و انتها راه‌های پیاده از راه‌های پیاده پرهیز می‌کند توسعه @@ -3954,15 +3954,15 @@ برای استفاده از قابلیت‌های ویرایشی، از طریق OAuth ثبت ورود کنید ثبت ورود از طریق OAuth پاک‌سازی توکن OAuth اوپن‌استریت‌مپ - ثبت خروج موفقیت‌آمیز بود + خروج ثبت شد فایل قبلاً در OsmAnd درون‌برد شده است استفاده از الگوریتم مسیریابی دومرحله‌ای A*‎ نمودار - دادهٔ %1$s فقط در جاده ارائه می‌شود. برای اینکه آن را داشته باشید با استفاده از «مسیریابی بین نقطه‌ها» مسیری محاسبه کنید. - منتظر بمانید تا مسیر دوباره محاسبه شود. -\nنمودار پس از بازمحاسبه در دسترس قرار خواهد گرفت. + دادهٔ %1$s فقط در جاده‌ها ارائه می‌شود. با استفاده از «مسیریابی بین نقطه‌ها» مسیری محاسبه کنید تا نمودارها را ببینید. + لطفاً منتظر بمانید. +\nنمودار پس از بازمحاسبهٔ مسیر فراهم می‌شود. %1$s — %2$s - دست‌کم باید دو نقطه اضافه کنید + دست‌کم دو نقطه اضافه کنید ثبت ورود در اوپن‌استریت‌مپ ثبت ورود در OpenStreetMap.org ثبت ورود از طریق اوپن‌استریت‌مپ @@ -3973,7 +3973,7 @@ حساب ثبت ورود مدیریت اشتراک - در رابطه با اشتراک شما مشکلی وجود دارد. روی دکمه کلیک کنید تا به تنظیمات اشتراک گوگل‌پلی بروید و شیوهٔ پرداخت خود را اصلاح نمایید. + روی دکمه بزنید و یک شیوهٔ پرداخت در گوگل‌پلی تنظیم کنید تا اشتراک خود را اصلاح نمایید. تاریخچهٔ نشانه‌ها ارسال فایل GPX به اوپن‌استریت‌مپ «عمومی» یا Public به این معنی است که رد به‌صورت عمومی در ردهای جی‌پی‌اس شما و لیست ردهای عمومی جی‌پی‌اس در قالب خام نمایش داده می‌شود. دادهٔ نقاط رد که از طریق API در دسترس قرار می‌گیرد، به صفحهٔ ردهای شما ارجاع نمی‌دهد. مهر زمان نقاط رد از طریق API جی‌پی‌اس‌های عمومی دست‌یافتنی نیست و نقاط رد بر اساس زمان مرتب نیستند. diff --git a/OsmAnd/res/values-gl/phrases.xml b/OsmAnd/res/values-gl/phrases.xml index c1185e530d..df06bf6a08 100644 --- a/OsmAnd/res/values-gl/phrases.xml +++ b/OsmAnd/res/values-gl/phrases.xml @@ -3618,7 +3618,7 @@ Acceso para vehículos: militar Acceso para vehículos: entregas Acceso para vehículos: forestal - Acceso para automóbiles: + Acceso para automóbiles: si Acceso para automóbiles: privado Acceso para automóbiles: non Acceso para automóbiles: destino @@ -3876,4 +3876,19 @@ Serea Enfermeira Axente de diñeiro móbil + Lavadoiro + Estación de transferencia de residuos + Balanza + Posto de garda forestal + Lago + Río + Pozo + Bomba motorizada + Tanque de auga + Billa + Tratamento de augas + Ben intubado + Vacinación: COVID19 + Vacinación + Base de salvavidas \ No newline at end of file diff --git a/OsmAnd/res/values-gl/strings.xml b/OsmAnd/res/values-gl/strings.xml index 6a334c14d7..7bcde5c6c2 100644 --- a/OsmAnd/res/values-gl/strings.xml +++ b/OsmAnd/res/values-gl/strings.xml @@ -1554,7 +1554,7 @@ Lon %2$s Publicado Precisas estar conectado para instalar este plugin. Obter - Recalcular só a parte inicial da ruta para viaxes longas. + Recalcular só a parte inicial da ruta, útil para viaxes longas. Gosta do OsmAnd? A túa opinión e comentarios son valorados. Avalía esta aplicación @@ -1967,7 +1967,7 @@ Lon %2$s Séguenos Inserir o enderezo do destino mediante son. Navegación do OsmAnd ao Vivo - Destino non definido + Complemento de accesibilidade: Destino non definido Anuncios automáticos intelixentes Notificar só cando se mude o enderezo ó destino. Período dos anuncios automáticos @@ -2594,7 +2594,7 @@ Lon %2$s O destino ubícase nunha área de acceso privado. Permitir o emprego de camiños privados para esta viaxe\? Mudar a procura ou aumentar o seu raio. Un botón que amosa ou agocha as notas do OSM no mapa. - Baixar o mapa de \'Sobreposición de asombreado\' para amosar o asombreado vertical. + Baixar o mapa de sobreposición de \'asombreado\' para amosar o asombreado vertical. Instalar o plugin de \'Curvas de nivel\' para amosar as áreas verticais graduadas. Agochar a partir do nivel de achegamento Baixar o mapa de \'Curvas de nivel\' para empregalas nesta rexión. @@ -3450,7 +3450,7 @@ Lon %2$s Nome de usuario e contrasinal Os axustes deste complemento son globais e aplícanse a todos os perfís Edición do OSM - Podes ollar todas as túas edicións aínda non subidas ou erros do OSM en %1$s. Os puntos subidos non se amosan no OsmAnd. + Mira todas as edicións aínda non subidas ou erros do OSM en %1$s. As modificacións xa subidas non se amosarán máis. OSM A icona amósase mentres se navega ou se move pola pantalla. A icona amósase en asueto. @@ -3887,7 +3887,7 @@ Lon %2$s Perfil de navegación Escolle un ficheiro de pista ó que se engadirá un novo segmento. Imaxes a nivel de rúa - Pechar o plan de ruta sen gardar\?, desbotaranse todas as modificacións. + Estás seguro de desbotar todas as modificacións no plan de ruta\? En caso de dirección inversa Gardar coma novo ficheiro de pista Engadir a un ficheiro de pistas @@ -3930,7 +3930,7 @@ Lon %2$s Nome: A – Z Iconas de inicio e fin Grazas por mercar \'Curvas de nivel\' - Subscrición cobrada por período escollido. Cancélaa na AppGallery en calquera intre. + A subscrición é cobrada por período escollido. Cancélaa na AppGallery en calquera intre. O pagamento será cargado na túa conta da AppGallery no mesmo intre da confirmación da compra. \n \nA subscrición é renovada de xeito automático a menos que sexa cancelada antes da data de renovación. A túa conta será cargada polo período de renovación (mes/trimestre/ano) só na data de renovación. @@ -3944,7 +3944,7 @@ Lon %2$s Enrutamento complexo Enrutamento de dúas fases para a navegación de automóbil Desenvolvemento do transporte público nativo - Activar cálculo de enrutamento de transporte público do Java (seguro) + Activar cálculo da ruta de transporte público do Java (seguro) Novidades Inicia a sesión co OAuth para empregar as funcións de edición do OSM Entrar polo OAuth @@ -3959,8 +3959,7 @@ Lon %2$s MGRS O OsmAnd emprega MGRS, que é semellante ó formato UTM NATO. %1$s datos dispoñíbeis só nas estrada, necesitas calcular unha ruta empregando \"Ruta entre puntos\" para obtela. - Agarda polo recálculo da ruta. -\nO gráfico estará dispoñíbel após o recálculo. + O gráfico estará dispoñíbel após o recálculo da ruta. Mapas locais Salto Instalación @@ -3971,9 +3970,9 @@ Lon %2$s Deporte Emerxencia Viaxe - Tes que engadir polo menos dous puntos + Engade polo menos dous puntos Xestionar subscrición - Hai un problema coa túa subscrición. Preme no botón para ir ós axustes de subscrición da Google Play e corrixir o teu método de pagamento. + Preme no botón para configurar un método de pagamento na Google Play e corrixir a túa subscrición. A subscrición do OsmAnd Live expirou A subscrición do OsmAnd Live foi detida A subscrición do OsmAnd Live está en espera @@ -4022,9 +4021,25 @@ Lon %2$s O OsmAnd amosa imaxes de diferentes fontes: \nOpenPlaceReviews - imaxes POI; \nMapillary - maxes a nivel de rúa; -\nWeb / Wikimedia - imaxes POI especificadas nos datos do OpenStreetMap. +\nWeb / Wikimedia - imaxes POI segundo datos do OpenStreetMap. %1$s * %2$s Alemán casual - Podes empregar os datos de elevación para considerar o ascenso ou descenso da túa viaxe + Podes empregar os datos de elevación para ter en conta o ascenso e descenso da viaxe Avión lixeiro + Unir segmentos + Dividir antes + Dividir despois + Engadir novo segmento + Perfil do OsmAnd + Perfil do usuario + • Engadiuse a opción de exportar e importar todos os datos, incluíndo os axustes, os recursos e \"Os meus sitios\" +\n +\n • Planificar ruta: engadíronse gráficos para os segmentos de pistas coa ruta e capacidade de crear ou editar pistas de segmentos múltiples +\n +\n • Engadiuse o método de autenticación OAuth para o OpenStreetMap e mellorouse a interface de usuario dos diálogos do OSM +\n +\n • Engadíronse cores personalizadas para os favoritos e os puntos de referencia da pista +\n +\n + Inverter todos os puntos \ No newline at end of file diff --git a/OsmAnd/res/values-hu/phrases.xml b/OsmAnd/res/values-hu/phrases.xml index 23b5710cb5..7c65c9a561 100644 --- a/OsmAnd/res/values-hu/phrases.xml +++ b/OsmAnd/res/values-hu/phrases.xml @@ -3878,4 +3878,16 @@ Vízimentő-támaszpont Oltóközpont: Covid19 Oltóközpont + Ranger station + Ruhamosó hely + Hulladékátrakodó + Hídmérleg + + Folyó + Kút + Motoros szivattyú + Víztartály + Csap + Vízmű + Csöves kút \ No newline at end of file diff --git a/OsmAnd/res/values-hu/strings.xml b/OsmAnd/res/values-hu/strings.xml index 875d3ad0d0..ad8a1e9fa4 100644 --- a/OsmAnd/res/values-hu/strings.xml +++ b/OsmAnd/res/values-hu/strings.xml @@ -1559,7 +1559,7 @@ galiciai észt szebuano - Régi, összeférhetetlen Wikipédia adataid vannak. Archiválod őket? + A Wikipédia-adatok régiek és inkompatbilisek. Archiválja őket\? Letölt további Wikipédia-adatokat (%1$s MB)\? spanyol (argentín) norvég bokmål @@ -1943,7 +1943,7 @@ Mágneses irány Ne tervezzen újra, ha letérsz az útvonalról Ne tervezzen újra, ha ellenkező irányba mész - Nincs beállítva célpont + Kisegítő lehetőségek bővítmény: nincs beállítva célpont Akkumulátor töltöttsége A jelölő helyének módosításához mozgasd a térképet @@ -2566,7 +2566,7 @@ Könyvjelző Teljes leírás elrejtése Teljes leírás megjelenítése - Wikipédia hivatkozás megnyitása online + Wikipédia-link megnyitása online A hivatkozás a böngészőben fog megnyílni. Wikipédia olvasása offline Összes letöltése @@ -2603,7 +2603,7 @@ Kép gyorsítótár Keresési előzmények törlése Képek letöltése - Szerezz OsmAnd Live előfizetést, hogy Wikipédia és Wikivoyage cikkeket olvashass offline. + Szerezzen OsmAnd Live előfizetést, hogy offline módon is olvashasson Wikipédia- és Wikivoyage-szócikkeket. Hogyan nyisd meg a linket? Lemondtad az OsmAnd Live előfizetésedet Előfizetés megújítása az alábbi funkciók használatának folytatásához: @@ -2640,14 +2640,14 @@ Wikivoyage szócikkek Köztes érkezési idő Köztes idő - Töltsd le %1$s Wikipédia szócikkeit offline olvasásra. + Töltse le %1$s Wikipédia szócikkeit offline olvasásra. Wikipédia adatok letöltése Szócikk megnyitása online Szócikk megtekintése böngészőben. - ez a régió + ezen régió Megfelelő szócikk keresése Szócikk nem található - Hogyan nyiss meg Wikipédia szócikkeket? + Hogyan lehet Wikipédia szócikkeket megnyitni\? Az összes funkció feloldásához szerezze be az OsmAnd Live-ot: Napi térképfrissítés korlátlan számú letöltéssel, az összes fizetős és ingyenes bővítmény, Wikipédia, Wikivoyage és sok más. Az alapértelmezett stílus módosítása a gyalogos és kerékpáros utak kontrasztosabb megjelenítése érdekében. Mapnik színeket használ. Topo stílus alapján terepen történő vezetéshez és alátétként zöld műholdképekkel történő használathoz. Vékonyabb főutak, vastagabb mezőgazdasági és erdészeti utak, ösvények, kerékpár- és más útvonalak. @@ -3422,7 +3422,7 @@ Felhasználónév és jelszó Ezek a bővítménybeállítások globálisak, és minden profilra vonatkoznak OSM-szerkesztés - Az összes még fel nem töltött szerkesztés vagy OSM-hiba megtekinthető itt: %1$s. A már feltöltött pontok nem láthatók az OsmAndban. + "A még fel nem töltött szerkesztéseket vagy OSM-hibákat megtekintheti itt: %1$s. A már feltöltött módosítások nem jelennek meg." OSM Navigáció vagy haladás közben megjelenő ikon. Álló helyzetben megjelenő ikon. @@ -3481,7 +3481,7 @@ A domborzatárnyékolás réteg sötét árnyalattal emeli ki a lejtőket, csúcsokat és a síkságokat. A lejtő réteg színezéssel jeleníti meg a terep meredekségét. Állítsa be a legkisebb és legnagyobb nagyítási szintet, amelyen a réteg megjelenjen. - További térképek szükségesek a domborzatárnyékolás térképen való megjelenítéséhez. + A domborzatárnyékolás térképen való megjelenítéséhez további térképek szükségesek. További térképek szükségesek a lejtők térképen való megjelenítéséhez. Áttetszőség Nagyítási szintek @@ -3543,8 +3543,8 @@ \n \nA használaton kívüli bővítmények kikapcsolásával elrejtheti őket a %1$s menüből. Menü elemek, Opciók - Jelölje ki a térképen megjelenő Wikipédia szócikkek nyelvét. A szócikk olvasása közben válthat valamennyi elérhető nyelv között. - Előfordulhat, hogy néhány Wikipédia szócikk nem érhető el az ön nyelvén. + Jelölje ki a térképen megjelenő Wikipédia-szócikkek nyelvét. A szócikk olvasása közben válthat valamennyi elérhető nyelv között. + Előfordulhat, hogy néhány Wikipédia-szócikk nem érhető el az ön nyelvén. Nyomvonalfájl kijelölése kantoni déli min @@ -3734,7 +3734,7 @@ Kérem, adjon nevet a pontnak Adja meg a járműve hosszát (a hosszú járművekre útvonalkorlátozások vonatkozhatnak). Legközelebbi célpont törlése - Wikipédia térképek letöltése + Wikipédia-térképek letöltése További adatokat is kijelölhet exportálásra a profillal együtt. Kiegészítő adatok átvétele Az importált profil kiegészítő adatokat is tartalmaz. Koppintson az \"Importál\" gombra kizálólag a profiladatok importálásához vagy válassza a kiegészítő adatokat. @@ -3744,8 +3744,8 @@ \nAz előfizetés automatikusan megújul, kivéve, ha azt a megújítási dátum előtt lemondja. A megújítási időszakra (hónap / 3 hónap / év) vonatkozóan a számláját csak a megújítás napján terheljük meg. \n \nAz előfizetéseket a Google Play beállításai között kezelheti és törölheti. - Törli az útvonal soron következő célpontját. Amennyiben ez a végző célpont, a navigáció megáll. - Tudjon meg többet az érdekes pontokról a Wikipédiából. Ez az Ön offline zsebútikönyve – egyszerűen kapcsolja be a Wikipédia-bővítményt, és élvezze az Ön körüli objektumokról szóló cikkeket. + Törli az útvonala következő célpontját. Ha ez a végső cél, akkor a navigáció leáll. + Az érdekes helyekről a Wikipédia segítségével tájékozódhat. Ez olyan, mint egy zsebútikönyv: helyekről és úti célokról olvashat benne. Salakmotor Robogó Kerekesszék @@ -3785,7 +3785,7 @@ Válassza ki, hogy a nyomvonalon milyen távolság- vagy időintervallumok jelzései jelenjenek meg. Ez a szűrő kiküszöböli az ismétlődő pontok rögzítését ott, ahol túl kevés tényleges mozgás történhetett, így szebb lesz a később utólag fel nem dolgozott nyomvonalak térbeli megjelenése. Felvétel vágása - Szünetelteti a nyomvonal naplózását, amikor az alkalmazást bezárják (a \'mostanában használt alkalmazások\' felületen). (Az OsmAnd háttérjelzése eltűnik az Android értesítési sávjáról.) + A nyomvonal naplózása szünetelni fog, amikor az alkalmazást (újabb alkalmazásokkal) kilövik. (Ilyenkor az OsmAnd háttérjelzése is eltűnik az Android értesítési sávjáról.) Javaslat: Először próbálja meg a mozgásérzékelést használni a legkisebb elmozdulást naplózó szűrővel (B). Ez jobb eredményeket hozhat, és kevesebb adatot veszít. Ha a nyomvonalak alacsony sebességnél továbbra is zajosak, próbálkozzon itt nullától eltérő értékekkel. Felhívjuk figyelmét, hogy egyes (hálózatalapú módszereket használó) mérőeszközök egyáltalán nem adnak sebességértéket, így ebben az esetben semmi sem fog rögzülni. Utcaszintű képek Csak az út vonala lesz elmentve, az útpontok törlődnek. @@ -3859,7 +3859,7 @@ Navigációs profil Megjegyzés a „sebesség > 0” ellenőrzéséhez: A legtöbb GPS lapkakészlet (chipset) csak akkor mutat sebességértéket, ha az algoritmus megállapítja, hogy mozgásban van. Ha nincs mozgás, akkor nem mutatnak sebességet. Ezért ebben a szűrőben a „> 0” beállítás bizonyos értelemben a GPS lapkakészlet mozgásérzékelését használja. De – még ha a rögzítés ideje alatt itt nem is szűrjük – a GPX elemzése során mégis használjuk ezt a funkciót a korrigált távolság meghatározásához, vagyis az abban a mezőben megjelenített érték a mozgás közben rögzített távolság. Csak a kijelölt szakasz lesz újraszámítva a kiválasztott profilnak megfelelően. - Biztos, hogy mentés nélkül bezárja az útvonaltervezőt\? Minden változást elvész. + Biztosan elveti az összes módosítást a tervezett útvonalon\? Válassza ki a követendő nyomvonalfájlt A felsorolt %1$s már létezik az OsmAndban. Minden korábbi szakasz @@ -3898,12 +3898,12 @@ Név: A–Z Kiindulás és érkezés ikonjai Köszönjük, hogy megvásárolta a szintvonalbővítményt (Contour lines) - Az előfizetés díja a kiválasztott időszakonként lesz kiszámítva. Bármikor törölheti az AppGallery webhelyen. - A fizetést a vásárlás visszaigazolásakor az AppGallery számlájára terheljük. + Az előfizetés a kiválasztott időszakonként kerül felszámolásra. Bármikor törölheti az AppGallery alkalmazásban. + AppGallery-fiókját a vásárlás visszaigazolása után terheljük meg. \n -\nAz előfizetés automatikusan megújul, kivéve, ha azt a megújítási dátum előtt lemondja. A megújítási időszakra (hónap / 3 hónap / év) vonatkozóan a számláját csak a megújítás napján terheljük meg. +\nAz előfizetés automatikusan meghosszabbodik, kivéve, ha a megújítási dátum előtt lemondják. A megújítási időszakra (hónap / három hónap / év) az Ön számláját csak a megújítás napján terheljük meg. \n -\nAz előfizetéseket az AppGallery beállításai között kezelheti és törölheti. +\nAz előfizetéseket az AppGallery beállításaiban kezelheti és törölheti. Gyalogutak elkerülése Gyalogutak elkerülése Fejlesztés @@ -3950,7 +3950,7 @@ Az OsmAnd-előfizetés fel van függesztve Az OsmAnd Live-előfizetés szünetel Az OsmAnd Live-előfizetés lejárt - Probléma van az előfizetésével. A fizetési mód kijavítása érdekében koppintson a gombra a Google Play előfizetési beállításaihoz történő ugráshoz. + Előfizetése kijavítása érdekében koppintson a gombra, és állítsa be a fizetési módot a Google Playen. Bejelentkezés Fiók Bejelentkezés felhasználónévvel és jelszóval @@ -4007,4 +4007,7 @@ \n • Egyéni színek támogatása a kedvenceknél és a nyomvonalak útpontjainál \n \n" + OsmAnd profil + Felhasználói profil + Összes pont megfordítása \ No newline at end of file diff --git a/OsmAnd/res/values-is/phrases.xml b/OsmAnd/res/values-is/phrases.xml index f83bcbafb9..fb51911ead 100644 --- a/OsmAnd/res/values-is/phrases.xml +++ b/OsmAnd/res/values-is/phrases.xml @@ -3610,7 +3610,7 @@ Aðgangur farartækja: hernaðarlegur Aðgangur farartækja: vöruafhending Aðgangur farartækja: skógrækt - Aðgangur bifreiða: + Aðgangur bifreiða: já Aðgangur bifreiða: einka Aðgangur bifreiða: nei Aðgangur bifreiða: áfangastaður @@ -3868,4 +3868,19 @@ Ræðismannastofnun Útibú Ræðisskrifstofa + Millifærslustöð fyrir úrgang + Aflknúin dæla + Vatnstankur + Vatnshani + Bólusetning + Landvarðastöð + Pípubrunnur + Vatnsvinnsla + Bólusetning: COVID19 + Brunnur + Vogarbrú + Stöðuvatn + Þvottaþró (þvottaaðstaða) + Á + Bækistöð öryggisvarða \ No newline at end of file diff --git a/OsmAnd/res/values-is/strings.xml b/OsmAnd/res/values-is/strings.xml index b0f780c060..a5e5e86d40 100644 --- a/OsmAnd/res/values-is/strings.xml +++ b/OsmAnd/res/values-is/strings.xml @@ -1085,7 +1085,7 @@ Nota Kalman-síu Nota segulskynjara Full útgáfa - Hafna leið + Hafna leið\? Stöðva leiðsögn Hreinsa út áfangastað Sérsníddu hvernig forritið lítur út. @@ -1384,7 +1384,7 @@ Ertu viss að þú viljir skipta út eftirlætinu %1$s? Fjarlægja valin atriði úr breytingaskránni? Breyta staðsetningu kortamerkis - Áfangastaður er ekki stilltur + Stillingaviðbót fyrir altækan aðgang: Áfangastaður er ekki stilltur Hlutfallsleg stefna Bættu við kortamerkjum á kortinu Nafnlausir notendur geta ekki: @@ -1746,7 +1746,7 @@ Stigvaxandi leit byggingar Upplýsingum um hnút var ekki hlaðið inn Snjöll endurreiknun leiðar - Endurreiknar aðeins upphafshluta leiðar. Má nota fyrir langar ferðir. + Endurreiknar aðeins upphafshluta leiðar, má nota fyrir langar ferðir. Kortið sem eingöngu er með vegum er ekki nauðsynlegt, þar sem þú ert þegar með staðlaða (fulla útgáfu) kortsins. Sækja það samt? Birta dýptarlínur og punkta. Dýptarlínur sjávar @@ -3429,7 +3429,7 @@ Rakning með netstuðningi Nákvæmni skráninga Endurreikningur leiðar - Þessar stillingar eru víðværar og eiga við um öll snið + Þessar stillingar viðbótarinnar eru víðværar og eiga við um öll snið OSM-breytingar OSM Táknið birtist þegar leiðsögn er í gangi eða á meðan verið er á ferðinni. @@ -3459,7 +3459,7 @@ Hljóð við myndatöku Heimild fékkst Veljið studda %1$s kerfisviðaukaskrá í staðinn. - Þú getur skoðað allar óinnsendar breytingar þínar eða villur í OSM í %1$s. Innsendir punktar eru ekki birtir í OsmAnd. + Þú getur skoðað allar óinnsendar breytingar þínar eða villur í OSM í %1$s. Áður innsendir punktar eru ekki lengur birtir. Aukaverkanir: Ferilinn þinn mun vanta alla þá hluta þar sem kröfur um lágmarkshraða voru ekki uppfylltar (t.d. þar sem þú ert að ýta hjólinu þínu upp bratta brekku). Að auki verða engar upplýsingar um kyrrstöður, eins og t.d. hvíldarstaði. Þetta hefur áhrif á alla greiningu eða eftirvinnslu, eins og þegar verið er að skilgreina heildarlengd ferðar, tíma á ferðinni eða meðalhraða þinn. Ábending: Reyndu fyrst að nota hreyfiskynjun í gegnum síuna sem mælis skráða lágmarkshliðrun (B), hún gæti gefið betri niðurstöður og þú tapar minna af gögnum. Ef ferlarnir þínir eru áfram loðnir við lágan hraða, skaltu prófa hér gildi sem ekki eru núll. Athugaðu að sumar mælingar gætu gefið gildi án hraða (sumar mæliaðferðir sem byggjast á netkerfum), í þeim tilfellum myndi ekkert vera skráð. Athugaðu: ef hraði > 0 skoðaðu: Flest GPS kubbasett gefa aðeins upp hraðagildi ef reikniritið ákvarðar að tækið sé á hreyfingu, en gefa ekki neitt gildi ef svo er ekki. Þar af leiðir að sé notuð stillingin > 0 í þessari síu má segja að sé verið að nota hreyfiskynjunina í GPS kubbasettinu. En jafnvel þó þetta sé ekki síað á meðan skráningu stendur, þá notum við samt þennan eiginleika í greiningu okkar á GPX-ferlinum til að ákvarða leiðrétta vegalengd, þ.e.a.s. gildið sem birtist í þeim reit er einmitt skráð vegalengd á meðan verið er á ferðinni. @@ -3788,7 +3788,7 @@ Eyða næsta markpunkti Leiðsagnarsnið Hjólastóll áfram - Náðu í upplýsingar um merka staði frá Wikipedia. Þetta er þá orðið að vasaleiðsögn án nettengingar - bara virkjaðu Wikipedia-viðbótina og njóttu þess að geta lesið um hlutina í kringum þig. + Náðu í upplýsingar um merka staði frá Wikipedia, þetta er þá orðið að vasaleiðsögn án nettengingar. Stýrðu aðdráttarstigi korts með hljóðstyrkshnöppum tækisins. Tilgreindu lengd farartækis sem leyfð er á leiðum. Ef stefna er öfug @@ -3840,7 +3840,7 @@ \n Veldu bilið þar sem merki með tíma eða vegalengd á ferlinum verða birt. Tengja við vegina - Ertu viss um að þú viljir loka leiðaskipulagningu án þess að vista\? + Ertu viss um að þú viljir henda öllum óvistuðum breytingum í skipulögðu leiðinni\? Vista sem ferilskrá Þolvik vegalengdar Skrifa feril í GPX-skrá @@ -3850,7 +3850,7 @@ Veldu hvernig þú vilt skipta upp: eftir tíma eða vegalengd. Vista sem nýja ferilskrá Veldu ferilskrá til að fylgja - Puntinum á núverandi áfangastað leiðarinnar verður eytt. Ef þetta er sjálfur áfangastaðurinn, mun leiðsögn stöðvast. + Eyðir næsta áfangastað leiðarinnar. Ef þetta er sjálfur áfangastaðurinn, mun leiðsögn stöðvast. Eyða heimilisfangi Ferlar Upphaf ferils @@ -3910,7 +3910,7 @@ Hreinsa OAuth-teikn OpenStreetMap Skráð út %s GPX-skrár valdar - Mun setja GPX-skráningu í bið þegar forritið er drepið (slökkt á því í gegnum skjáinn fyrir nýleg forrit - bakgrunnsvísir OsmAnd hverfur þar með úr tilkynningastiku Android-kerfisins.) + GPX-skráning er sett í bið þegar forritið er drepið (slökkt á því í gegnum skjáinn fyrir nýleg forrit - bakgrunnsvísir OsmAnd hverfur þar með úr tilkynningastiku Android-kerfisins.) Veldu millibil skráninga í almenna leiðarskráningu (virkjað með viðmótshlutanum \'Skráning ferðar\' á kortinu). Setja skráningu í bið Halda áfram með skráningu @@ -3929,10 +3929,10 @@ \n • Lagfærð vandamál við inn/útflutning á stillingum sniða \n \n - Bíddu eftir endurútreikningi leiðar. + Bíddu aðeins. \nGrafið verður tiltækt eftir endurútreikning. Fyrir akstur vélsleða á sérstökum vegum og slóðum. - %1$s gögn aðeins tiltæk á vegunum, þú þarft að reikna leið með “Leið milli punkta” til að fá hana. + %1$s gögn aðeins tiltæk á vegunum, þú þarft að reikna leið með “Leið milli punkta” til að sjá gröf. Skrá hefur þegar verið flutt inn í OsmAnd Bil Takk fyrir að kaupa \'Hæðarlínur\' @@ -3957,7 +3957,7 @@ Þú þarft að skrá þig inn til að geta sent inn ný atriði eða breytingar. \n \nÞú getur skráð þig inn með öruggu OAuth-aðferðinni eða notað notandanafn og lykilorð innskráningar. - Það er vandamál með áskriftina þína. Smelltu á hnappinn til að fara í stillingar Google Play áskriftarinnar og laga þar greiðslumátann. + Smelltu á hnappinn til að setja upp greiðslumáta á Google Play áskriftarinnar og laga þar áskriftina þína. Nota notandanafn og lykilorð Skrá inn á OpenStreetMap Skrá inn @@ -3969,7 +3969,7 @@ Notandaaðgangur Skrá inn á OpenStreetMap.org Vinnsluferill kortamerkja - Þú þarft að bæta við a.m.k. tveimur punktum + Bættu við a.m.k. tveimur punktum MGRS Áskrift að OsmAnd Live hefur verið sett í bið OsmAnd notar MGRS, sem er svipað og UTM-snið NATO. @@ -3988,7 +3988,7 @@ Búa til nýjan notandaaðgang Þú getur skráð þig inn með öruggu OAuth-aðferðinni eða notað notandanafn og lykilorð innskráningar. Vélbátar - Ljósmyndir eru í boði frá opna gagnaverkefninu OpenPlaceReviews.org. Til að geta sent inn myndir þarftu að skrá þig á vefsvæðinu. + Ljósmyndir eru í boði frá opna gagnaverkefninu OpenPlaceReviews.org. Til að geta sent inn myndir þarftu að skrá þig á vefsvæðinu þeirra. Gera athugasemd við OSM-minnispunkt \"Rekjanlegt\" þýðir að ferillinn birtist ekki á neinum opinberum listum, en meðhöndlaðir ferilpunktar með tímamerkjum úr ferlinum (sem ekki er hægt að tengja beint við þig) munu birtast í gögnum sem eru sótt í gegnum opinbert GPS API-forritsviðmót. Þú getur notað hæðargögn til útreikninga á hækkun/lækkun í ferðunum þínum @@ -4013,4 +4013,20 @@ Veldu þau gögn sem á að flytja út í skrána. Veldu mynd Mistókst að senda inn mynd, reyndu aftur síðar + • Bætt við möguleika á að flytja út og inn öll gögn, þar með talið stillingar, tilföng, staðir +\n +\n • Skipulagning leiða: gröf ferilbúta með leið, bætt við möguleika á að búa til og breyta mörgum ferilbútum +\n +\n • Bætt við OAuth-auðkenningarleið fyrir OpenStreetMap, endurbætt viðmót valglugga í OSM +\n +\n • Stuðningur við sérsniðna liti í eftirlætum og ferilpunktum +\n +\n + Sameina búta + Skipta upp á eftir + Bæta við nýjum bút + Skipta upp á undan + Notandasnið + Snúa við öllum punktum + OsmAnd-snið \ No newline at end of file diff --git a/OsmAnd/res/values-iw/phrases.xml b/OsmAnd/res/values-iw/phrases.xml index 6ad03cdbfe..758fd725f0 100644 --- a/OsmAnd/res/values-iw/phrases.xml +++ b/OsmAnd/res/values-iw/phrases.xml @@ -2190,24 +2190,77 @@ חץ רטט לחץ - - - - - - - - - - - - - - - - - - - - + אתר נופש + מחנה קיץ + מלח: אין + מלח + עומק + עץ אופניים + תאים + נקודת בידוק צבאית + סוג ארון: תאורת רחוב + סוג ארון: ניהול מים + סוג ארון: פסולת + סוג ארון: שירות דואר + סוג ארון: גז + סוג ארון: טלוויזיה בכבלים + סוג ארון: תקשורת + סוג ארון: חשמל + מספק שירות: כן + זרם מים + בריכה + ראשי + טרוליבוס + מונית שירות + אוטובוס + פני השטח: שלג + פני השטח: מלח + סדרת פיצוצים + תאריך הפיצוץ (UTC) + סוג פיצוץ: תת־ימי + סוג פיצוץ: תת־קרקעי + ניקוי אופניים: אין + ניקוי + כלים לתיקון עצמי של אופניים: אין + כלים לתיקון עצמי + משאבת אופניים: אין + משאבה + השכרת אופניים: לא + השכרה + תיקון אופניים: לא + תיקון + קמעונאות אופניים: לא + קמעונאי + עם חנות + מעקב: יש + פריט היסטורי + רכבת תחתית + מעבורת + פוניקולר + מונורייל + רכבת קלה + רכבת + חשמלית + סוג: ייצור לבנים + סוג: מבשלה + סוג: רכבים לפירוק + סוג: מחסן + סוג: מזקקה + סוג: עץ + סוג: תעשיית הגז + סוג: מפעל + סוג: wellsite + סוג: תעשיית הדלק + הכשרה: עיצוב שיער + הכשרה: תעופה + הכשרה: יוגה + הכשרה: מחשבים + הכשרה: בישול + הכשרה: אומנות + הכשרה: ספורט + הכשרה: ריקוד + הכשרה: מוזיקה + הכשרה: שפה + הכשרה: אומנות לחימה + סוג: חקלאות \ No newline at end of file diff --git a/OsmAnd/res/values-iw/strings.xml b/OsmAnd/res/values-iw/strings.xml index 66d06edf99..0faf759a72 100644 --- a/OsmAnd/res/values-iw/strings.xml +++ b/OsmAnd/res/values-iw/strings.xml @@ -3561,7 +3561,7 @@ מעויני ריבועי מזערי - ניתן לצפות בכל העריכות או בתקלות ב־OSM שטרם הועלו תחת %1$s. נקודות שהועלו לא מופיעות ב־OsmAnd. + ניתן לצפות בכל העריכות או בתקלות ב־OSM שטרם הועלו תחת %1$s. שינויים שהועלו לא יופיעו עוד. מפת הצללה עם צבעי הצללה כהים כדי להדגיש מדרונות, פסגות ועמקים. מתומן הערות ה־OSM שלך הן תחת %1$s. @@ -3800,7 +3800,7 @@ נא לספק את אורך כלי הרכב שלך, מגבלות מסלול מסוימות עשויות לחול על כלי רכב ארוכים. מחיקת נקודת היעד הקרובה ביותר נא לספק שם לנקודה - נקודת היעד הנוכחית במסלול תימחק. אם זה יהיה היעד, הניווט ייעצר. + מוחק את נקודת היעד הבאה במסלול שלך. אם זו נקודת היעד, הניווט ייפסק. הורדת מפות ויקיפדיה קבלת מידע על נקודות עניין מוויקיפדיה. מדריך הכיס הפרטי שלך - עליך פשוט להפעיל את התוסף של ויקיפדיה וליהנות מערכים על מה שסביבך. אופנוע שטח @@ -3877,7 +3877,7 @@ רק קו המסלול יישמר, נקודות הדרך תימחקנה. שם קובץ %s קובצי מסלול נבחרו - רישום ה־GPX יושהה כאשר היישומון ייסגר (דרך היישומונים האחרונים). (התראות הרקע של OsmAnd תיעלמנה מסרגל ההודעות של Android.) + תיעוד המסלול יושהה כאשר היישומון ייסגר (דרך היישומונים האחרונים). (התראות הרקע של OsmAnd תיעלמנה מסרגל ההודעות של Android.) להשהות הקלטת מסלול להמשיך בהקלטת המסלול בררת המחדל של המערכת @@ -3914,11 +3914,11 @@ הימנעות משבילי הולכי רגל הימנעות משבילי הולכי רגל המינוי חויב לתקופה הנבחרת. ניתן לבטל דרך ה־AppGallery בכל עת. - התשלום יחויב דרך חשבון ה־AppGallery שלך עם אישור הרכישה. + חשבון ה־AppGallery שלך יחויב עם אישור הרכישה. \n -\nהמינוי מתחדש אוטומטי אלא אם כן בוטל בטרם תאריך החידוש. החשבון שלך יחויב על תקופת החידוש (חודש/שלושה חודשים/שנה) רק בתאריך החידוש. +\nתוקף המינוי מתארך אוטומטי אלא אם כן בוטל בטרם תאריך החידוש. החשבון שלך יחויב על תקופת החידוש (חודש/שלושה חודשים/שנה) רק בתאריך החידוש. \n -\nניתן לנהל ולבטל את המינויים שלך דרך ההגדרות ב־AppGallery שלך. +\nניתן לנהל ולבטל את המינויים שלך בהגדרות ב־AppGallery שלך. מה חדש פיתוח נתוני זמן אמת של OsmAnd @@ -4020,5 +4020,7 @@ \n פרופיל פרופיל - להפוך את כל + להפוך את כל הנקודות + נא לבחור את הפרופיל בו יעשה שימוש עם הפעלת היישומון. + שימוש אחרון \ No newline at end of file diff --git a/OsmAnd/res/values-pt-rBR/phrases.xml b/OsmAnd/res/values-pt-rBR/phrases.xml index c74187b593..cea52cd7ef 100644 --- a/OsmAnd/res/values-pt-rBR/phrases.xml +++ b/OsmAnd/res/values-pt-rBR/phrases.xml @@ -3896,4 +3896,5 @@ Bem embalado Balança Posto de guarda florestal + Estação de transferência de resíduos \ No newline at end of file diff --git a/OsmAnd/res/values-pt-rBR/strings.xml b/OsmAnd/res/values-pt-rBR/strings.xml index 331af6bd93..af7e266361 100644 --- a/OsmAnd/res/values-pt-rBR/strings.xml +++ b/OsmAnd/res/values-pt-rBR/strings.xml @@ -4013,4 +4013,5 @@ Adicionar novo segmento Perfil do OsmAnd Perfil de usuário + Reverter todos os pontos \ No newline at end of file diff --git a/OsmAnd/res/values-pt/phrases.xml b/OsmAnd/res/values-pt/phrases.xml index 97cf62dc34..f523f432af 100644 --- a/OsmAnd/res/values-pt/phrases.xml +++ b/OsmAnd/res/values-pt/phrases.xml @@ -3871,4 +3871,11 @@ Base de salva-vidas Vacinação: COVID19 Vacinação + Estação de transferência de resíduos + Ponte de peso + Lago + Rio + Tanque de água + Torneira + Estação de tratamento de água \ No newline at end of file diff --git a/OsmAnd/res/values-ru/phrases.xml b/OsmAnd/res/values-ru/phrases.xml index 1430becd00..1cdda217e1 100644 --- a/OsmAnd/res/values-ru/phrases.xml +++ b/OsmAnd/res/values-ru/phrases.xml @@ -3881,4 +3881,9 @@ Нажмите Водозабор Трубчатый колодец + Мобильный платёжный офис + Весовая платформа + Умывальник + Станция перекачки отходов + Станция рейнджеров \ No newline at end of file diff --git a/OsmAnd/res/values-ru/strings.xml b/OsmAnd/res/values-ru/strings.xml index 8ca4200ddd..c7dbab21d9 100644 --- a/OsmAnd/res/values-ru/strings.xml +++ b/OsmAnd/res/values-ru/strings.xml @@ -1819,7 +1819,7 @@ Предельная высота Укажите высоту транспортного средства для учёта при построении маршрута. Умный пересчёт маршрута - Пересчитывается только начальная часть маршрута. Может использоваться для дальних поездок. + Пересчитывается только начальная часть маршрута. Полезно для дальних поездок. Удалить правки OSM Выключено Раскраска по сетевой принадлежности @@ -2169,7 +2169,7 @@ Предотвращает пересчёт маршрута после того, как вы из него вышли. Не пересчитывать маршрут при обратном направлении движения Предотвращает автоматический пересчёт маршрута при обратном направлении движения. - Пункт назначения не задан + Плагин специальных возможностей: пункт назначения не задан Озвучивать направление Индицировать звуком направление на целевую точку. Тактильная индикация направления @@ -3505,7 +3505,7 @@ Имя пользователя и пароль Эти настройки плагина применяются ко всем профилям Редактирование OSM - Здесь отображаются ваши правки и ошибки OSM, ещё не отправленные в %1$s. Отправленные точки не будут отображаться. + Здесь отображаются ваши правки и ошибки OSM, ещё не отправленные в %1$s. Отправленные изменения не отображаются. OSM Значок, отображаемый во время навигации или движения. Значок, отображаемый в состоянии покоя. @@ -3905,7 +3905,7 @@ Спасибо за покупку \'Контурных линий\' Избегать пешеходных дорожек Избегать пешеходных дорожек - Подписка взимается за выбранный период. Отмените её в AppGallery в любое время. + Подписка оплачивается за выбранный период. Отмените её в AppGallery в любое время. Оплата будет снята с вашей учётной записи AppGallery при подтверждении покупки. \n \nПодписка продлевается автоматически, если она не будет отменена до даты продления. С вашего счёта будет взиматься плата за период продления (месяц/три месяца/год) только в дату продления. @@ -3941,7 +3941,7 @@ MGRS OsmAnd использует MGRS, который похож на формат UTM NATO. Развитие местного общественного транспорта - Нужно добавить не менее двух точек + Добавьте не менее двух точек Данные %1$s доступны только для дорог, нужно рассчитать маршрут с помощью «Маршрут между точками». Вводите теги через запятую. Перейти на расчёт маршрутов общественного транспорта с помощью Java (safe) @@ -3974,7 +3974,7 @@ OsmAnd показывает фотографии из нескольких источников: \nOpenPlaceReviews — фотогорафии POI; \nMapillary — изображения улиц; -\nWeb / Wikimedia — фотографии POI, указанные в данных OpenStreetMap. +\nWeb / Wikimedia — фотографии POI по данным OpenStreetMap. Ресурсы Примерный размер файла Требуется для импорта @@ -3985,10 +3985,10 @@ Выберите импортируемые элементы. Использовать dev.openstreetmap.org Можно войти в систему с помощью безопасного метода OAuth или используя свои имя пользователя и пароль. - Возникла проблема с подпиской. Нажмите кнопку, чтобы перейти к настройкам подписки Google Play и исправить способ оплаты. + Нажмите кнопку, чтобы перейти к настройкам подписки Google Play и исправить способ оплаты. Комментировать заметку OSM Переключиться на использование dev.openstreetmap.org вместо openstreetmap.org для тестирования отправки заметок OSM / POI / GPX. - Фотографии предоставлены проектом открытых данных OpenPlaceReviews.org. Чтобы отправить свои фотографии, необходимо зарегистрироваться на сайте. + Фотографии предоставлены проектом открытых данных OpenPlaceReviews.org. Чтобы отправить свои фотографии, необходимо зарегистрироваться на этом сайте. Невозможно отправить изображение, попробуйте позже Выбор изображения Каяк @@ -3999,4 +3999,6 @@ Объединить сегменты Разделить до Разделить после + Профиль пользователя + Профиль OsmAnd \ No newline at end of file diff --git a/OsmAnd/res/values-sk/strings.xml b/OsmAnd/res/values-sk/strings.xml index d798541848..422e385aca 100644 --- a/OsmAnd/res/values-sk/strings.xml +++ b/OsmAnd/res/values-sk/strings.xml @@ -4018,4 +4018,5 @@ \n Profil OsmAnd Profil používateľa + Otočiť všetky body \ No newline at end of file diff --git a/OsmAnd/res/values-tr/strings.xml b/OsmAnd/res/values-tr/strings.xml index 4be2bd1df1..91c6567731 100644 --- a/OsmAnd/res/values-tr/strings.xml +++ b/OsmAnd/res/values-tr/strings.xml @@ -3974,4 +3974,6 @@ OsmAnd profili Kullanıcı profili Tüm noktaları tersine çevir + Uygulama başlangıcında kullanılacak profili seçin. + Son kullanılan \ No newline at end of file diff --git a/OsmAnd/res/values-uk/strings.xml b/OsmAnd/res/values-uk/strings.xml index 0ea2ee3374..72b06030df 100644 --- a/OsmAnd/res/values-uk/strings.xml +++ b/OsmAnd/res/values-uk/strings.xml @@ -4015,4 +4015,6 @@ Профіль OsmAnd Профіль користувача Повернути назад всі точки + Виберіть профіль, який застосовуватиметься під час запуску. + Востаннє використовувалось \ No newline at end of file diff --git a/OsmAnd/res/values-zh-rTW/phrases.xml b/OsmAnd/res/values-zh-rTW/phrases.xml index ca44b815e7..e675bb2d0b 100644 --- a/OsmAnd/res/values-zh-rTW/phrases.xml +++ b/OsmAnd/res/values-zh-rTW/phrases.xml @@ -3895,4 +3895,6 @@ 管井 地磅 護林員站 + 公共洗衣區 + 垃圾站 \ No newline at end of file diff --git a/OsmAnd/res/values-zh-rTW/strings.xml b/OsmAnd/res/values-zh-rTW/strings.xml index 990ccc8531..dfbbb9bb93 100644 --- a/OsmAnd/res/values-zh-rTW/strings.xml +++ b/OsmAnd/res/values-zh-rTW/strings.xml @@ -4013,4 +4013,5 @@ \n OsmAnd 設定檔 使用者設定檔 + 反轉所有點 \ No newline at end of file diff --git a/OsmAnd/res/values/strings.xml b/OsmAnd/res/values/strings.xml index b1df50c808..95fc35bcd4 100644 --- a/OsmAnd/res/values/strings.xml +++ b/OsmAnd/res/values/strings.xml @@ -11,6 +11,10 @@ Thx - Hardy --> + Prefer hiking routes + Prefer hiking routes + Last used + Select the profile, that will be used on application start. Reverse all points • Added option to export and import all data including settings, resources, my places\n\n diff --git a/OsmAnd/res/xml/global_settings.xml b/OsmAnd/res/xml/global_settings.xml index 5f2fef7d02..7ba1dce420 100644 --- a/OsmAnd/res/xml/global_settings.xml +++ b/OsmAnd/res/xml/global_settings.xml @@ -4,9 +4,10 @@ xmlns:tools="http://schemas.android.com/tools" android:title="@string/osmand_settings"> - diff --git a/OsmAnd/src/net/osmand/aidl/OsmandAidlApi.java b/OsmAnd/src/net/osmand/aidl/OsmandAidlApi.java index 15f7dd123b..88307a259e 100644 --- a/OsmAnd/src/net/osmand/aidl/OsmandAidlApi.java +++ b/OsmAnd/src/net/osmand/aidl/OsmandAidlApi.java @@ -80,6 +80,7 @@ import net.osmand.plus.routing.IRoutingDataUpdateListener; import net.osmand.plus.routing.RouteCalculationResult.NextDirectionInfo; import net.osmand.plus.routing.RouteDirectionInfo; import net.osmand.plus.routing.RoutingHelper; +import net.osmand.plus.routing.RoutingHelperUtils; import net.osmand.plus.routing.VoiceRouter; import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.settings.backend.ApplicationMode.ApplicationModeBean; @@ -1803,7 +1804,7 @@ public class OsmandAidlApi { if (ni.directionInfo != null && ni.directionInfo.getTurnType() != null) { TurnType tt = ni.directionInfo.getTurnType(); RouteDirectionInfo a = ni.directionInfo; - bundle.putString(prefix + PARAM_NT_DIRECTION_NAME, RoutingHelper.formatStreetName(a.getStreetName(), a.getRef(), a.getDestinationName(), "")); + bundle.putString(prefix + PARAM_NT_DIRECTION_NAME, RoutingHelperUtils.formatStreetName(a.getStreetName(), a.getRef(), a.getDestinationName(), "")); bundle.putString(prefix + PARAM_NT_DIRECTION_TURN, tt.toXmlString()); bundle.putFloat(prefix + PARAM_NT_DIRECTION_ANGLE, tt.getTurnAngle()); bundle.putBoolean(prefix + PARAM_NT_DIRECTION_POSSIBLY_LEFT, tt.isPossibleLeftTurn()); @@ -2365,7 +2366,7 @@ public class OsmandAidlApi { if (!ApplicationMode.values(app).contains(appMode)) { ApplicationMode.changeProfileAvailability(appMode, true, app); } - app.getSettings().APPLICATION_MODE.set(appMode); + app.getSettings().setApplicationMode(appMode); } }); return true; @@ -2395,7 +2396,7 @@ public class OsmandAidlApi { public boolean addRoadBlock(ABlockedRoad road) { LatLon latLon = new LatLon(road.getLatitude(), road.getLongitude()); - app.getAvoidSpecificRoads().addImpassableRoad(null, latLon, false, false, null); + app.getAvoidSpecificRoads().addImpassableRoad(null, latLon, false, false, road.getAppModeKey()); return true; } diff --git a/OsmAnd/src/net/osmand/plus/AppInitializer.java b/OsmAnd/src/net/osmand/plus/AppInitializer.java index c372a4de98..eda57e3c28 100644 --- a/OsmAnd/src/net/osmand/plus/AppInitializer.java +++ b/OsmAnd/src/net/osmand/plus/AppInitializer.java @@ -419,10 +419,10 @@ public class AppInitializer implements IProgress { if (osmandSettings.FOLLOW_THE_ROUTE.get()) { ApplicationMode savedMode = osmandSettings.readApplicationMode(); if (!osmandSettings.APPLICATION_MODE.get().getStringKey().equals(savedMode.getStringKey())) { - osmandSettings.APPLICATION_MODE.set(savedMode); + osmandSettings.setApplicationMode(savedMode); } } else { - osmandSettings.APPLICATION_MODE.set(osmandSettings.DEFAULT_APPLICATION_MODE.get()); + osmandSettings.setApplicationMode(osmandSettings.DEFAULT_APPLICATION_MODE.get()); } startTime = System.currentTimeMillis(); getLazyRoutingConfig(); diff --git a/OsmAnd/src/net/osmand/plus/ContextMenuAdapter.java b/OsmAnd/src/net/osmand/plus/ContextMenuAdapter.java index d188a7d0ee..381125e0ec 100644 --- a/OsmAnd/src/net/osmand/plus/ContextMenuAdapter.java +++ b/OsmAnd/src/net/osmand/plus/ContextMenuAdapter.java @@ -258,7 +258,7 @@ public class ContextMenuAdapter { @Override public void onClick(View view) { if (selected.size() > 0) { - app.getSettings().APPLICATION_MODE.set(selected.iterator().next()); + app.getSettings().setApplicationMode(selected.iterator().next()); notifyDataSetChanged(); } if (changeAppModeListener != null) { diff --git a/OsmAnd/src/net/osmand/plus/OsmAndLocationProvider.java b/OsmAnd/src/net/osmand/plus/OsmAndLocationProvider.java index c465ce2f77..2d35556d84 100644 --- a/OsmAnd/src/net/osmand/plus/OsmAndLocationProvider.java +++ b/OsmAnd/src/net/osmand/plus/OsmAndLocationProvider.java @@ -39,7 +39,7 @@ import net.osmand.data.LatLon; import net.osmand.data.QuadPoint; import net.osmand.plus.TargetPointsHelper.TargetPoint; import net.osmand.plus.routing.RoutingHelper; -import net.osmand.plus.routing.RoutingHelper.RouteSegmentSearchResult; +import net.osmand.plus.routing.RouteSegmentSearchResult; import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.router.RouteSegmentResult; @@ -154,7 +154,7 @@ public class OsmAndLocationProvider implements SensorEventListener { startLocation.setTime(ms); } RouteSegmentSearchResult searchResult = - RoutingHelper.searchRouteSegment(currentLocation.getLatitude(), currentLocation.getLongitude(), -1, roads); + RouteSegmentSearchResult.searchRouteSegment(currentLocation.getLatitude(), currentLocation.getLongitude(), -1, roads); if (searchResult != null) { currentRoad = searchResult.getRoadIndex(); currentSegment = searchResult.getSegmentIndex(); diff --git a/OsmAnd/src/net/osmand/plus/OsmandApplication.java b/OsmAnd/src/net/osmand/plus/OsmandApplication.java index 36c566a7a6..db7c0d6433 100644 --- a/OsmAnd/src/net/osmand/plus/OsmandApplication.java +++ b/OsmAnd/src/net/osmand/plus/OsmandApplication.java @@ -64,6 +64,7 @@ import net.osmand.plus.helpers.enums.MetricsConstants; import net.osmand.plus.inapp.InAppPurchaseHelper; import net.osmand.plus.mapmarkers.MapMarkersDbHelper; import net.osmand.plus.mapmarkers.MapMarkersHelper; +import net.osmand.plus.measurementtool.MeasurementEditingContext; import net.osmand.plus.monitoring.LiveMonitoringHelper; import net.osmand.plus.osmedit.oauth.OsmOAuthHelper; import net.osmand.plus.poi.PoiFiltersHelper; @@ -156,6 +157,7 @@ public class OsmandApplication extends MultiDexApplication { GpxDbHelper gpxDbHelper; QuickActionRegistry quickActionRegistry; OsmOAuthHelper osmOAuthHelper; + MeasurementEditingContext measurementEditingContext; private Resources localizedResources; private Map customRoutingConfigs = new ConcurrentHashMap<>(); @@ -465,6 +467,14 @@ public class OsmandApplication extends MultiDexApplication { return routingHelper; } + public MeasurementEditingContext getMeasurementEditingContext() { + return measurementEditingContext; + } + + public void setMeasurementEditingContext(MeasurementEditingContext context) { + this.measurementEditingContext = context; + } + public TransportRoutingHelper getTransportRoutingHelper() { return transportRoutingHelper; } @@ -537,7 +547,7 @@ public class OsmandApplication extends MultiDexApplication { routingHelper.clearCurrentRoute(null, new ArrayList()); routingHelper.setRoutePlanningMode(false); osmandSettings.LAST_ROUTING_APPLICATION_MODE = osmandSettings.APPLICATION_MODE.get(); - osmandSettings.APPLICATION_MODE.set(osmandSettings.DEFAULT_APPLICATION_MODE.get()); + osmandSettings.setApplicationMode(osmandSettings.DEFAULT_APPLICATION_MODE.get()); targetPointsHelper.removeAllWayPoints(false, false); } diff --git a/OsmAnd/src/net/osmand/plus/TargetPointsHelper.java b/OsmAnd/src/net/osmand/plus/TargetPointsHelper.java index 4221ffa308..2d46b63fd1 100644 --- a/OsmAnd/src/net/osmand/plus/TargetPointsHelper.java +++ b/OsmAnd/src/net/osmand/plus/TargetPointsHelper.java @@ -12,6 +12,7 @@ import net.osmand.data.PointDescription; import net.osmand.plus.GeocodingLookupService.AddressLookupRequest; import net.osmand.plus.routing.RouteProvider.RouteService; import net.osmand.plus.routing.RoutingHelper; +import net.osmand.plus.routing.RoutingHelperUtils; import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.settings.backend.OsmAndAppCustomization.OsmAndAppCustomizationListener; import net.osmand.plus.settings.backend.OsmandSettings; @@ -444,7 +445,7 @@ public class TargetPointsHelper { Location lastKnownLocation = ctx.getLocationProvider().getLastKnownLocation(); LatLon latLon = lastKnownLocation != null ? new LatLon(lastKnownLocation.getLatitude(), lastKnownLocation.getLongitude()) : null; - routingHelper.checkAndUpdateStartLocation(latLon); + RoutingHelperUtils.checkAndUpdateStartLocation(ctx, latLon); setMyLocationPoint(latLon, false, null); } } diff --git a/OsmAnd/src/net/osmand/plus/activities/MapActivity.java b/OsmAnd/src/net/osmand/plus/activities/MapActivity.java index 8541ab5e87..e41792225b 100644 --- a/OsmAnd/src/net/osmand/plus/activities/MapActivity.java +++ b/OsmAnd/src/net/osmand/plus/activities/MapActivity.java @@ -126,7 +126,7 @@ import net.osmand.plus.routepreparationmenu.MapRouteInfoMenu; import net.osmand.plus.routepreparationmenu.MapRouteInfoMenuFragment; import net.osmand.plus.routing.IRouteInformationListener; import net.osmand.plus.routing.RoutingHelper; -import net.osmand.plus.routing.RoutingHelper.RouteCalculationProgressCallback; +import net.osmand.plus.routing.RouteCalculationProgressCallback; import net.osmand.plus.routing.TransportRoutingHelper.TransportRouteCalculationProgressCallback; import net.osmand.plus.search.QuickSearchDialogFragment; import net.osmand.plus.search.QuickSearchDialogFragment.QuickSearchTab; @@ -553,7 +553,7 @@ public class MapActivity extends OsmandActionBarActivity implements DownloadEven allowPrivate.setModeValue(mode, true); } } - getRoutingHelper().recalculateRouteDueToSettingsChange(); + getRoutingHelper().onSettingsChanged(null, true); } }); dlg.setNegativeButton(R.string.shared_string_no, null); diff --git a/OsmAnd/src/net/osmand/plus/activities/MapActivityActions.java b/OsmAnd/src/net/osmand/plus/activities/MapActivityActions.java index ed97a3669e..3ed8053ca4 100644 --- a/OsmAnd/src/net/osmand/plus/activities/MapActivityActions.java +++ b/OsmAnd/src/net/osmand/plus/activities/MapActivityActions.java @@ -63,6 +63,7 @@ import net.osmand.plus.measurementtool.MeasurementToolFragment; import net.osmand.plus.measurementtool.StartPlanRouteBottomSheet; import net.osmand.plus.monitoring.OsmandMonitoringPlugin; import net.osmand.plus.osmedit.dialogs.DismissRouteBottomSheetFragment; +import net.osmand.plus.profiles.ProfileDataUtils; import net.osmand.plus.profiles.RoutingProfileDataObject; import net.osmand.plus.routepreparationmenu.MapRouteInfoMenu; import net.osmand.plus.routepreparationmenu.WaypointsFragment; @@ -121,7 +122,6 @@ import static net.osmand.aidlapi.OsmAndCustomizationConstants.MAP_CONTEXT_MENU_S import static net.osmand.plus.ContextMenuAdapter.PROFILES_CHOSEN_PROFILE_TAG; import static net.osmand.plus.ContextMenuAdapter.PROFILES_CONTROL_BUTTON_TAG; import static net.osmand.plus.ContextMenuAdapter.PROFILES_NORMAL_PROFILE_TAG; -import static net.osmand.plus.settings.fragments.NavigationFragment.getRoutingProfiles; public class MapActivityActions implements DialogProvider { @@ -555,7 +555,7 @@ public class MapActivityActions implements DialogProvider { TargetPointsHelper targets = app.getTargetPointsHelper(); ApplicationMode mode = appMode != null ? appMode : getRouteMode(from); - //app.getSettings().APPLICATION_MODE.set(mode); + //app.getSettings().setApplicationMode(mode, false); app.getRoutingHelper().setAppMode(mode); app.initVoiceCommandPlayer(mapActivity, mode, true, null, false, false, showMenu); // save application mode controls @@ -588,7 +588,7 @@ public class MapActivityActions implements DialogProvider { TargetPointsHelper targets = app.getTargetPointsHelper(); ApplicationMode mode = getRouteMode(null); - //app.getSettings().APPLICATION_MODE.set(mode); + //app.getSettings().setApplicationMode(mode, false); app.getRoutingHelper().setAppMode(mode); //Test for #2810: No need to init player here? //app.initVoiceCommandPlayer(mapActivity, true, null, false, false); @@ -737,7 +737,7 @@ public class MapActivityActions implements DialogProvider { String modeDescription; - Map profilesObjects = getRoutingProfiles(app); + Map profilesObjects = ProfileDataUtils.getRoutingProfiles(app); for (final ApplicationMode appMode : activeModes) { if (appMode.isCustomProfile()) { modeDescription = getProfileDescription(app, appMode, profilesObjects, getString(R.string.profile_type_user_string)); @@ -756,7 +756,7 @@ public class MapActivityActions implements DialogProvider { .setListener(new ItemClickListener() { @Override public boolean onContextMenuClick(ArrayAdapter adapter, int itemId, int position, boolean isChecked, int[] viewCoordinates) { - app.getSettings().APPLICATION_MODE.set(appMode); + app.getSettings().setApplicationMode(appMode); updateDrawerMenu(); return false; } @@ -1046,7 +1046,7 @@ public class MapActivityActions implements DialogProvider { //switch profile button ApplicationMode currentMode = app.getSettings().APPLICATION_MODE.get(); String modeDescription; - Map profilesObjects = getRoutingProfiles(app); + Map profilesObjects = ProfileDataUtils.getRoutingProfiles(app); if (currentMode.isCustomProfile()) { modeDescription = getProfileDescription(app, currentMode, profilesObjects, getString(R.string.profile_type_user_string)); } else { diff --git a/OsmAnd/src/net/osmand/plus/base/MapViewTrackingUtilities.java b/OsmAnd/src/net/osmand/plus/base/MapViewTrackingUtilities.java index 9c6c6c2e7e..fa66a5c91a 100644 --- a/OsmAnd/src/net/osmand/plus/base/MapViewTrackingUtilities.java +++ b/OsmAnd/src/net/osmand/plus/base/MapViewTrackingUtilities.java @@ -25,6 +25,7 @@ import net.osmand.plus.dashboard.DashboardOnMap; import net.osmand.plus.mapcontextmenu.MapContextMenu; import net.osmand.plus.mapcontextmenu.other.TrackDetailsMenu; import net.osmand.plus.routing.RoutingHelper; +import net.osmand.plus.routing.RoutingHelperUtils; import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.views.AnimateDraggingMapThread; import net.osmand.plus.views.OsmandMapTileView; @@ -175,7 +176,7 @@ public class MapViewTrackingUtilities implements OsmAndLocationListener, IMapLoc locationProvider = location.getProvider(); if (settings.DRIVING_REGION_AUTOMATIC.get() && !drivingRegionUpdated && !app.isApplicationInitializing()) { drivingRegionUpdated = true; - app.getRoutingHelper().checkAndUpdateStartLocation(location); + RoutingHelperUtils.checkAndUpdateStartLocation(app, location); } } if (mapView != null) { diff --git a/OsmAnd/src/net/osmand/plus/dialogs/PluginInstalledBottomSheetDialog.java b/OsmAnd/src/net/osmand/plus/dialogs/PluginInstalledBottomSheetDialog.java index 0ef7154d2b..db9422859a 100644 --- a/OsmAnd/src/net/osmand/plus/dialogs/PluginInstalledBottomSheetDialog.java +++ b/OsmAnd/src/net/osmand/plus/dialogs/PluginInstalledBottomSheetDialog.java @@ -15,6 +15,7 @@ import androidx.fragment.app.FragmentManager; import net.osmand.AndroidUtils; import net.osmand.PlatformUtil; +import net.osmand.plus.profiles.ProfileDataUtils; import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandPlugin; @@ -33,7 +34,6 @@ import net.osmand.plus.download.DownloadValidationManager; import net.osmand.plus.download.IndexItem; import net.osmand.plus.helpers.AndroidUiHelper; import net.osmand.plus.helpers.FontCache; -import net.osmand.plus.settings.fragments.BaseSettingsFragment; import net.osmand.plus.widgets.style.CustomTypefaceSpan; import org.apache.commons.logging.Log; @@ -221,7 +221,7 @@ public class PluginInstalledBottomSheetDialog extends MenuBottomSheetDialogFragm final BottomSheetItemWithCompoundButton[] appModeItem = new BottomSheetItemWithCompoundButton[1]; appModeItem[0] = (BottomSheetItemWithCompoundButton) new BottomSheetItemWithCompoundButton.Builder() .setChecked(ApplicationMode.values(app).contains(mode)) - .setDescription(BaseSettingsFragment.getAppModeDescription(app, mode)) + .setDescription(ProfileDataUtils.getAppModeDescription(app, mode)) .setTitle(mode.toHumanString()) .setIcon(getActiveIcon(mode.getIconRes())) .setLayoutId(R.layout.bottom_sheet_item_with_descr_and_switch_56dp) diff --git a/OsmAnd/src/net/osmand/plus/helpers/AvoidSpecificRoads.java b/OsmAnd/src/net/osmand/plus/helpers/AvoidSpecificRoads.java index bfa84b8474..3a1f024155 100644 --- a/OsmAnd/src/net/osmand/plus/helpers/AvoidSpecificRoads.java +++ b/OsmAnd/src/net/osmand/plus/helpers/AvoidSpecificRoads.java @@ -32,8 +32,8 @@ import net.osmand.plus.R; import net.osmand.plus.UiUtilities; import net.osmand.plus.activities.MapActivity; import net.osmand.plus.mapcontextmenu.MapContextMenu; -import net.osmand.plus.routing.RoutingHelper; -import net.osmand.plus.routing.RoutingHelper.RouteSegmentSearchResult; +import net.osmand.plus.routing.RouteSegmentSearchResult; +import net.osmand.plus.routing.RoutingHelperUtils; import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.views.layers.ContextMenuLayer; import net.osmand.router.RouteSegmentResult; @@ -119,7 +119,7 @@ public class AvoidSpecificRoads { remove(item); removeImpassableRoad(item); notifyDataSetChanged(); - recalculateRoute(); + recalculateRoute(item != null ? item.appModeKey : null); } }); return v; @@ -150,7 +150,7 @@ public class AvoidSpecificRoads { if (obj != null) { String locale = app.getSettings().MAP_PREFERRED_LOCALE.get(); boolean transliterate = app.getSettings().MAP_TRANSLITERATE_NAMES.get(); - String name = RoutingHelper.formatStreetName( + String name = RoutingHelperUtils.formatStreetName( obj.getName(locale, transliterate), obj.getRef(locale, transliterate, true), obj.getDestinationName(locale, transliterate, true), @@ -163,11 +163,9 @@ public class AvoidSpecificRoads { return app.getString(R.string.shared_string_road); } - private void recalculateRoute() { - RoutingHelper rh = app.getRoutingHelper(); - if (rh.isRouteCalculated() || rh.isRouteBeingCalculated()) { - rh.recalculateRouteDueToSettingsChange(); - } + private void recalculateRoute(@Nullable String appModeKey) { + ApplicationMode mode = ApplicationMode.valueOfStringKey(appModeKey, null); + app.getRoutingHelper().onSettingsChanged(mode); } public void removeImpassableRoad(LatLon latLon) { @@ -184,7 +182,7 @@ public class AvoidSpecificRoads { removeImpassableRoad(getLocation(obj)); } - public void showDialog(@NonNull final MapActivity mapActivity) { + public void showDialog(@NonNull final MapActivity mapActivity, final @Nullable ApplicationMode mode) { boolean nightMode = app.getDaynightHelper().isNightModeForMapControls(); Context themedContext = UiUtilities.getThemedContext(mapActivity, nightMode); @@ -209,14 +207,14 @@ public class AvoidSpecificRoads { bld.setPositiveButton(R.string.shared_string_select_on_map, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { - selectFromMap(mapActivity); + selectFromMap(mapActivity, mode); } }); bld.setNegativeButton(R.string.shared_string_close, null); bld.show(); } - public void selectFromMap(final MapActivity mapActivity) { + public void selectFromMap(@NonNull final MapActivity mapActivity) { ContextMenuLayer cm = mapActivity.getMapLayers().getContextMenuLayer(); cm.setSelectOnMap(new CallbackWithObject() { @Override @@ -227,6 +225,17 @@ public class AvoidSpecificRoads { }); } + public void selectFromMap(@NonNull final MapActivity mapActivity, @Nullable final ApplicationMode mode) { + ContextMenuLayer cm = mapActivity.getMapLayers().getContextMenuLayer(); + cm.setSelectOnMap(new CallbackWithObject() { + @Override + public boolean processResult(LatLon result) { + addImpassableRoad(mapActivity, result, true, false, mode != null ? mode.getStringKey() : null); + return true; + } + }); + } + public void addImpassableRoad(@Nullable final MapActivity mapActivity, @NonNull final LatLon loc, final boolean showDialog, @@ -239,12 +248,14 @@ public class AvoidSpecificRoads { ApplicationMode defaultAppMode = app.getRoutingHelper().getAppMode(); final ApplicationMode appMode = appModeKey != null ? ApplicationMode.valueOfStringKey(appModeKey, defaultAppMode) : defaultAppMode; - List roads = app.getRoutingHelper().getRoute().getOriginalRoute(); + List roads = app.getMeasurementEditingContext() != null + ? app.getMeasurementEditingContext().getRoadSegmentData(appMode) + : app.getRoutingHelper().getRoute().getOriginalRoute(); if (mapActivity != null && roads != null) { RotatedTileBox tb = mapActivity.getMapView().getCurrentRotatedTileBox().copy(); float maxDistPx = MAX_AVOID_ROUTE_SEARCH_RADIUS_DP * tb.getDensity(); RouteSegmentSearchResult searchResult = - RoutingHelper.searchRouteSegment(loc.getLatitude(), loc.getLongitude(), maxDistPx / tb.getPixDensity(), roads); + RouteSegmentSearchResult.searchRouteSegment(loc.getLatitude(), loc.getLongitude(), maxDistPx / tb.getPixDensity(), roads); if (searchResult != null) { QuadPoint point = searchResult.getPoint(); LatLon newLoc = new LatLon(MapUtils.get31LatitudeY((int) point.y), MapUtils.get31LongitudeX((int) point.x)); @@ -349,10 +360,10 @@ public class AvoidSpecificRoads { app.getSettings().removeImpassableRoad(location); } } - recalculateRoute(); + recalculateRoute(avoidRoadInfo.appModeKey); if (activity != null) { if (showDialog) { - showDialog(activity); + showDialog(activity, ApplicationMode.valueOfStringKey(avoidRoadInfo.appModeKey, null)); } MapContextMenu menu = activity.getContextMenu(); if (menu.isActive()) { diff --git a/OsmAnd/src/net/osmand/plus/helpers/ExternalApiHelper.java b/OsmAnd/src/net/osmand/plus/helpers/ExternalApiHelper.java index a81540be89..d0d2b6dbd7 100644 --- a/OsmAnd/src/net/osmand/plus/helpers/ExternalApiHelper.java +++ b/OsmAnd/src/net/osmand/plus/helpers/ExternalApiHelper.java @@ -47,6 +47,7 @@ import net.osmand.plus.quickaction.QuickActionRegistry; import net.osmand.plus.routing.RouteCalculationResult.NextDirectionInfo; import net.osmand.plus.routing.RouteDirectionInfo; import net.osmand.plus.routing.RoutingHelper; +import net.osmand.plus.routing.RoutingHelperUtils; import net.osmand.plus.search.listitems.QuickSearchListItem; import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.track.SaveGpxAsyncTask; @@ -697,7 +698,7 @@ public class ExternalApiHelper { if (ni.directionInfo != null && ni.directionInfo.getTurnType() != null) { TurnType tt = ni.directionInfo.getTurnType(); RouteDirectionInfo a = ni.directionInfo; - result.putExtra(prefix + PARAM_NT_DIRECTION_NAME, RoutingHelper.formatStreetName(a.getStreetName(), a.getRef(), a.getDestinationName(), "")); + result.putExtra(prefix + PARAM_NT_DIRECTION_NAME, RoutingHelperUtils.formatStreetName(a.getStreetName(), a.getRef(), a.getDestinationName(), "")); result.putExtra(prefix + PARAM_NT_DIRECTION_TURN, tt.toXmlString()); result.putExtra(prefix + PARAM_NT_DIRECTION_ANGLE, tt.getTurnAngle()); result.putExtra(prefix + PARAM_NT_DIRECTION_POSSIBLY_LEFT, tt.isPossibleLeftTurn()); @@ -736,7 +737,7 @@ public class ExternalApiHelper { OsmandApplication app = mapActivity.getMyApplication(); RoutingHelper routingHelper = app.getRoutingHelper(); if (gpx == null) { - app.getSettings().APPLICATION_MODE.set(mode); + app.getSettings().setApplicationMode(mode); final TargetPointsHelper targets = mapActivity.getMyApplication().getTargetPointsHelper(); targets.removeAllWayPoints(false, true); targets.navigateToPoint(to, true, -1, toDesc); @@ -746,7 +747,7 @@ public class ExternalApiHelper { mapActivity.getMapRouteInfoMenu().show(); } else { if (app.getSettings().APPLICATION_MODE.get() != routingHelper.getAppMode()) { - app.getSettings().APPLICATION_MODE.set(routingHelper.getAppMode()); + app.getSettings().setApplicationMode(routingHelper.getAppMode(), false); } mapActivity.getMapViewTrackingUtilities().backToLocationImpl(); app.getSettings().FOLLOW_THE_ROUTE.set(true); diff --git a/OsmAnd/src/net/osmand/plus/mapcontextmenu/MapContextMenu.java b/OsmAnd/src/net/osmand/plus/mapcontextmenu/MapContextMenu.java index 1bb1e1146e..c1a6a6cc0a 100644 --- a/OsmAnd/src/net/osmand/plus/mapcontextmenu/MapContextMenu.java +++ b/OsmAnd/src/net/osmand/plus/mapcontextmenu/MapContextMenu.java @@ -953,7 +953,7 @@ public class MapContextMenu extends MenuTitleController implements StateChangedL MapActivity mapActivity = getMapActivity(); if (mapActivity != null) { if (navigateInPedestrianMode()) { - mapActivity.getMyApplication().getSettings().APPLICATION_MODE.set(ApplicationMode.PEDESTRIAN); + mapActivity.getMyApplication().getSettings().setApplicationMode(ApplicationMode.PEDESTRIAN, false); } mapActivity.getMapLayers().getMapControlsLayer().navigateButton(); } diff --git a/OsmAnd/src/net/osmand/plus/mapcontextmenu/controllers/ImpassibleRoadsMenuController.java b/OsmAnd/src/net/osmand/plus/mapcontextmenu/controllers/ImpassibleRoadsMenuController.java index b4779f1d27..450d89f093 100644 --- a/OsmAnd/src/net/osmand/plus/mapcontextmenu/controllers/ImpassibleRoadsMenuController.java +++ b/OsmAnd/src/net/osmand/plus/mapcontextmenu/controllers/ImpassibleRoadsMenuController.java @@ -31,10 +31,7 @@ public class ImpassibleRoadsMenuController extends MenuController { if (activity != null) { app.getAvoidSpecificRoads().removeImpassableRoad( ImpassibleRoadsMenuController.this.avoidRoadInfo); - RoutingHelper rh = app.getRoutingHelper(); - if (rh.isRouteCalculated() || rh.isRouteBeingCalculated()) { - rh.recalculateRouteDueToSettingsChange(); - } + app.getRoutingHelper().onSettingsChanged(); activity.getContextMenu().close(); } } diff --git a/OsmAnd/src/net/osmand/plus/mapcontextmenu/other/DestinationReachedMenuFragment.java b/OsmAnd/src/net/osmand/plus/mapcontextmenu/other/DestinationReachedMenuFragment.java index 0bd0ca59d2..951e9a1b52 100644 --- a/OsmAnd/src/net/osmand/plus/mapcontextmenu/other/DestinationReachedMenuFragment.java +++ b/OsmAnd/src/net/osmand/plus/mapcontextmenu/other/DestinationReachedMenuFragment.java @@ -178,7 +178,7 @@ public class DestinationReachedMenuFragment extends Fragment { } } OsmandSettings settings = mapActivity.getMyApplication().getSettings(); - settings.APPLICATION_MODE.set(settings.DEFAULT_APPLICATION_MODE.get()); + settings.setApplicationMode(settings.DEFAULT_APPLICATION_MODE.get()); mapActivity.getMapActions().stopNavigationWithoutConfirm(); dismissMenu(); } diff --git a/OsmAnd/src/net/osmand/plus/mapcontextmenu/other/RoutePreferencesMenu.java b/OsmAnd/src/net/osmand/plus/mapcontextmenu/other/RoutePreferencesMenu.java index 65656cec79..005433cbe2 100644 --- a/OsmAnd/src/net/osmand/plus/mapcontextmenu/other/RoutePreferencesMenu.java +++ b/OsmAnd/src/net/osmand/plus/mapcontextmenu/other/RoutePreferencesMenu.java @@ -137,7 +137,7 @@ public class RoutePreferencesMenu { LocalRoutingParameter rp = group.getRoutingParameters().get(i); rp.setSelected(settings, i == position); } - mapActivity.getRoutingHelper().recalculateRouteDueToSettingsChange(); + mapActivity.getRoutingHelper().onSettingsChanged(true); updateParameters(); } } @@ -366,7 +366,7 @@ public class RoutePreferencesMenu { app.getTargetPointsHelper().updateRouteAndRefresh(true); updateSpinnerItems(gpxSpinner); updateParameters(); - mapActivity.getRoutingHelper().recalculateRouteDueToSettingsChange(); + mapActivity.getRoutingHelper().onSettingsChanged(true); return true; } }, app.getDaynightHelper().isNightModeForMapControls()); @@ -389,7 +389,7 @@ public class RoutePreferencesMenu { if (mapActivity.getRoutingHelper().getCurrentGPXRoute() != null) { mapActivity.getRoutingHelper().setGpxParams(null); settings.FOLLOW_THE_GPX_ROUTE.set(null); - mapActivity.getRoutingHelper().recalculateRouteDueToSettingsChange(); + mapActivity.getRoutingHelper().onSettingsChanged(true); } updateParameters(); return true; diff --git a/OsmAnd/src/net/osmand/plus/mapmarkers/MarkersPlanRouteContext.java b/OsmAnd/src/net/osmand/plus/mapmarkers/MarkersPlanRouteContext.java index 622fa39542..2060140d3d 100644 --- a/OsmAnd/src/net/osmand/plus/mapmarkers/MarkersPlanRouteContext.java +++ b/OsmAnd/src/net/osmand/plus/mapmarkers/MarkersPlanRouteContext.java @@ -4,6 +4,7 @@ import android.util.Pair; import net.osmand.Location; import net.osmand.data.LatLon; +import net.osmand.plus.routing.RouteCalculationProgressCallback; import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.GPXUtilities.TrkSegment; import net.osmand.GPXUtilities.WptPt; @@ -127,7 +128,7 @@ public class MarkersPlanRouteContext { findPairsToCalculate(points); RoutingHelper routingHelper = app.getRoutingHelper(); if (!snapToRoadPairsToCalculate.isEmpty() && !routingHelper.isRouteBeingCalculated()) { - routingHelper.startRouteCalculationThread(getParams(), true, true); + routingHelper.startRouteCalculationThread(getParams()); app.runInUIThread(new Runnable() { @Override public void run() { @@ -217,7 +218,7 @@ public class MarkersPlanRouteContext { params.mode = snappedMode; params.ctx = app; params.calculationProgress = calculationProgress = new RouteCalculationProgress(); - params.calculationProgressCallback = new RoutingHelper.RouteCalculationProgressCallback() { + params.calculationProgressCallback = new RouteCalculationProgressCallback() { @Override public void start() { @@ -265,7 +266,7 @@ public class MarkersPlanRouteContext { } }); if (!snapToRoadPairsToCalculate.isEmpty()) { - app.getRoutingHelper().startRouteCalculationThread(getParams(), true, true); + app.getRoutingHelper().startRouteCalculationThread(getParams()); } else { app.runInUIThread(new Runnable() { @Override diff --git a/OsmAnd/src/net/osmand/plus/measurementtool/GraphsCard.java b/OsmAnd/src/net/osmand/plus/measurementtool/GraphsCard.java index deb636420a..0e9e68b494 100644 --- a/OsmAnd/src/net/osmand/plus/measurementtool/GraphsCard.java +++ b/OsmAnd/src/net/osmand/plus/measurementtool/GraphsCard.java @@ -135,12 +135,12 @@ public class GraphsCard extends BaseCard implements OnUpdateInfoListener { } private void updateMenu() { - if (!editingCtx.isPointsEnoughToCalculateRoute()) { - graphTypesMenu.setVisibility(View.GONE); - } else { + if (editingCtx.isPointsEnoughToCalculateRoute()) { graphTypesMenu.setVisibility(View.VISIBLE); graphTypesMenu.removeAllViews(); fillInMenu(); + } else { + graphTypesMenu.setVisibility(View.GONE); } } @@ -349,7 +349,7 @@ public class GraphsCard extends BaseCard implements OnUpdateInfoListener { private List calculateRouteStatistics() { OsmandApplication app = getMyApplication(); - List route = editingCtx.getAllRouteSegments(); + List route = editingCtx.getOrderedRoadSegmentData(); if (route != null && app != null) { return RouteDetailsFragment.calculateRouteStatistics(app, route, nightMode); } diff --git a/OsmAnd/src/net/osmand/plus/measurementtool/MeasurementEditingContext.java b/OsmAnd/src/net/osmand/plus/measurementtool/MeasurementEditingContext.java index 6d1522de1d..74094cd07b 100644 --- a/OsmAnd/src/net/osmand/plus/measurementtool/MeasurementEditingContext.java +++ b/OsmAnd/src/net/osmand/plus/measurementtool/MeasurementEditingContext.java @@ -16,11 +16,12 @@ import net.osmand.plus.OsmandApplication; import net.osmand.plus.measurementtool.command.ApplyGpxApproximationCommand; import net.osmand.plus.measurementtool.command.MeasurementCommandManager; import net.osmand.plus.measurementtool.command.MeasurementModeCommand; +import net.osmand.plus.routing.IRouteSettingsListener; import net.osmand.plus.routing.RouteCalculationParams; import net.osmand.plus.routing.RouteCalculationParams.RouteCalculationResultListener; +import net.osmand.plus.routing.RouteCalculationProgressCallback; import net.osmand.plus.routing.RouteCalculationResult; import net.osmand.plus.routing.RoutingHelper; -import net.osmand.plus.routing.RoutingHelper.RouteCalculationProgressCallback; import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.router.RouteCalculationProgress; import net.osmand.router.RouteExporter; @@ -40,12 +41,13 @@ import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; import static net.osmand.plus.measurementtool.MeasurementEditingContext.CalculationMode.WHOLE_TRACK; import static net.osmand.plus.measurementtool.command.MeasurementModeCommand.MeasurementCommandType.APPROXIMATE_POINTS; -public class MeasurementEditingContext { +public class MeasurementEditingContext implements IRouteSettingsListener { public final static ApplicationMode DEFAULT_APP_MODE = ApplicationMode.DEFAULT; @@ -70,9 +72,11 @@ public class MeasurementEditingContext { private int calculatedPairs; private int pointsToCalculateSize; private CalculationMode lastCalculationMode = WHOLE_TRACK; - private SnapToRoadProgressListener progressListener; private ApplicationMode appMode = DEFAULT_APP_MODE; + + private SnapToRoadProgressListener progressListener; private RouteCalculationProgress calculationProgress; + private Map, RoadSegmentData> roadSegmentData = new ConcurrentHashMap<>(); public enum CalculationMode { @@ -86,64 +90,20 @@ public class MeasurementEditingContext { ADD_BEFORE, } - public static class RoadSegmentData { - private final ApplicationMode appMode; - private final WptPt start; - private final WptPt end; - private final List points; - private final List segments; - private final double distance; + public void setApplication(OsmandApplication application) { + this.application = application; + } - public RoadSegmentData(@NonNull ApplicationMode appMode, @NonNull WptPt start, @NonNull WptPt end, - @Nullable List points, @Nullable List segments) { - this.appMode = appMode; - this.start = start; - this.end = end; - this.points = points; - this.segments = segments; - double distance = 0; - if (points != null && points.size() > 1) { - for (int i = 1; i < points.size(); i++) { - distance += MapUtils.getDistance(points.get(i - 1).lat, points.get(i - 1).lon, - points.get(i).lat, points.get(i).lon); - } - } else if (segments != null) { - for (RouteSegmentResult segment : segments) { - distance += segment.getDistance(); - } - } - this.distance = distance; - } - - public ApplicationMode getAppMode() { - return appMode; - } - - public WptPt getStart() { - return start; - } - - public WptPt getEnd() { - return end; - } - - @Nullable - public List getPoints() { - return points != null ? Collections.unmodifiableList(points) : null; - } - - @Nullable - public List getSegments() { - return segments != null ? Collections.unmodifiableList(segments) : null; - } - - public double getDistance() { - return distance; + public void setupRouteSettingsListener() { + if (application != null) { + application.getRoutingHelper().addRouteSettingsListener(this); } } - public void setApplication(OsmandApplication application) { - this.application = application; + public void resetRouteSettingsListener() { + if (application != null) { + application.getRoutingHelper().removeRouteSettingsListener(this); + } } MeasurementCommandManager getCommandManager() { @@ -371,7 +331,7 @@ public class MeasurementEditingContext { return getPointsCount() >= 2; } - public List getAllRouteSegments() { + public List getOrderedRoadSegmentData() { List allSegments = new ArrayList<>(); for (Pair key : getOrderedRoadSegmentDataKeys()) { RoadSegmentData data = roadSegmentData.get(key); @@ -385,19 +345,48 @@ public class MeasurementEditingContext { return allSegments.size() > 0 ? allSegments : null; } - public void recalculateRouteSegmentsForAppMode() { - clearRouteSegmentsByAppMode(); - updateSegmentsForSnap(); - } - - public void clearRouteSegmentsByAppMode() { - for (Pair key : getOrderedRoadSegmentDataKeys()) { - if(key.first.getProfileType().equals(appMode.getStringKey())) { - RoadSegmentData data = roadSegmentData.get(key); - if (data != null) { - roadSegmentData.remove(key); + @NonNull + public List getRoadSegmentData(@Nullable ApplicationMode mode) { + List res = new ArrayList<>(); + if (mode == null) { + for (RoadSegmentData data : roadSegmentData.values()) { + List segments = data.getSegments(); + if (!Algorithms.isEmpty(segments)) { + res.addAll(segments); } } + } else { + for (Entry, RoadSegmentData> dataEntry : roadSegmentData.entrySet()) { + WptPt firstPoint = dataEntry.getKey().first; + if (mode.getStringKey().equals(firstPoint.getProfileType())) { + List segments = dataEntry.getValue().getSegments(); + if (!Algorithms.isEmpty(segments)) { + res.addAll(segments); + } + } + } + } + return res; + } + + void recalculateRouteSegments(@Nullable ApplicationMode mode) { + boolean changed = false; + if (mode == null) { + roadSegmentData.clear(); + changed = true; + } else { + for (Pair pair : getOrderedRoadSegmentDataKeys()) { + if (mode.getStringKey().equals(pair.first.getProfileType())) { + RoadSegmentData data = roadSegmentData.get(pair); + if (data != null) { + roadSegmentData.remove(pair); + changed = true; + } + } + } + } + if (changed) { + updateSegmentsForSnap(false); } } @@ -673,7 +662,7 @@ public class MeasurementEditingContext { if (progressListener != null && !routingHelper.isRouteBeingCalculated()) { RouteCalculationParams params = getParams(true); if (params != null) { - routingHelper.startRouteCalculationThread(params, true, true); + routingHelper.startRouteCalculationThread(params); application.runInUIThread(new Runnable() { @Override public void run() { @@ -794,11 +783,11 @@ public class MeasurementEditingContext { Pair pair = new Pair<>(routePoints.get(i), routePoints.get(i + 1)); int startIndex = pair.first.getTrkPtIndex(); if (startIndex < 0 || startIndex < prevPointIndex || startIndex >= points.size()) { - startIndex = findPointIndex(pair.first, points, prevPointIndex); + startIndex = MeasurementEditingContextUtils.findPointIndex(pair.first, points, prevPointIndex); } int endIndex = pair.second.getTrkPtIndex(); if (endIndex < 0 || endIndex < startIndex || endIndex >= points.size()) { - endIndex = findPointIndex(pair.second, points, startIndex); + endIndex = MeasurementEditingContextUtils.findPointIndex(pair.second, points, startIndex); } if (startIndex >= 0 && endIndex >= 0) { List pairPoints = new ArrayList<>(); @@ -847,7 +836,7 @@ public class MeasurementEditingContext { List gpxPoints = gpxApproximation.finalPoints; for (int i = 0; i < gpxPoints.size(); i++) { GpxPoint gp1 = gpxPoints.get(i); - boolean lastGpxPoint = isLastGpxPoint(gpxPoints, i); + boolean lastGpxPoint = MeasurementEditingContextUtils.isLastGpxPoint(gpxPoints, i); List points = new ArrayList<>(); List segments = new ArrayList<>(); for (int k = 0; k < gp1.routeToTarget.size(); k++) { @@ -858,7 +847,7 @@ public class MeasurementEditingContext { } for (int k = 0; k < segments.size(); k++) { RouteSegmentResult seg = segments.get(k); - fillPointsArray(points, seg, lastGpxPoint && k == segments.size() - 1); + MeasurementEditingContextUtils.fillPointsArray(points, seg, lastGpxPoint && k == segments.size() - 1); } if (!points.isEmpty()) { WptPt wp1 = new WptPt(); @@ -893,61 +882,6 @@ public class MeasurementEditingContext { return routePoints; } - private boolean isLastGpxPoint(List gpxPoints, int index) { - if (index == gpxPoints.size() - 1) { - return true; - } else { - for (int i = index + 1; i < gpxPoints.size(); i++) { - GpxPoint gp = gpxPoints.get(i); - for (int k = 0; k < gp.routeToTarget.size(); k++) { - RouteSegmentResult seg = gp.routeToTarget.get(k); - if (seg.getStartPointIndex() != seg.getEndPointIndex()) { - return false; - } - } - - } - } - return true; - } - - private void fillPointsArray(List points, RouteSegmentResult seg, boolean includeEndPoint) { - int ind = seg.getStartPointIndex(); - boolean plus = seg.isForwardDirection(); - float[] heightArray = seg.getObject().calculateHeightArray(); - while (ind != seg.getEndPointIndex()) { - addPointToArray(points, seg, ind, heightArray); - ind = plus ? ind + 1 : ind - 1; - } - if (includeEndPoint) { - addPointToArray(points, seg, ind, heightArray); - } - } - - private void addPointToArray(List points, RouteSegmentResult seg, int index, float[] heightArray) { - LatLon l = seg.getPoint(index); - WptPt pt = new WptPt(); - if (heightArray != null && heightArray.length > index * 2 + 1) { - pt.ele = heightArray[index * 2 + 1]; - } - pt.lat = l.getLatitude(); - pt.lon = l.getLongitude(); - points.add(pt); - } - - private int findPointIndex(WptPt point, List points, int firstIndex) { - double minDistance = Double.MAX_VALUE; - int index = 0; - for (int i = Math.max(0, firstIndex); i < points.size(); i++) { - double distance = MapUtils.getDistance(point.lat, point.lon, points.get(i).lat, points.get(i).lon); - if (distance < minDistance) { - minDistance = distance; - index = i; - } - } - return index; - } - private void updateSegmentsForSnap(boolean both) { recreateSegments(beforeSegments = new ArrayList<>(), beforeSegmentsForSnap = new ArrayList<>(), before.points, true); @@ -966,7 +900,6 @@ public class MeasurementEditingContext { } } - void cancelSnapToRoad() { progressListener.hideProgressBar(); if (calculationProgress != null) { @@ -1063,7 +996,7 @@ public class MeasurementEditingContext { progressListener.refresh(); RouteCalculationParams params = getParams(false); if (params != null) { - application.getRoutingHelper().startRouteCalculationThread(params, true, true); + application.getRoutingHelper().startRouteCalculationThread(params); } else { progressListener.hideProgressBar(); } @@ -1108,8 +1041,10 @@ public class MeasurementEditingContext { for (int i = startPointIndex; i < endPointIndex; i++) { Pair pair = new Pair<>(before.points.get(i), before.points.get(i + 1)); RoadSegmentData data = this.roadSegmentData.get(pair); - if (data != null && data.points != null && data.segments != null) { - for (WptPt pt : data.points) { + List dataPoints = data != null ? data.getPoints() : null; + List dataSegments = data != null ? data.getSegments() : null; + if (dataPoints != null && dataSegments != null) { + for (WptPt pt : dataPoints) { Location l = new Location(""); l.setLatitude(pt.getLatitude()); l.setLongitude(pt.getLongitude()); @@ -1119,7 +1054,7 @@ public class MeasurementEditingContext { locations.add(l); } pair.second.setTrkPtIndex(i + 1 < before.points.size() - 1 ? locations.size() : locations.size() - 1); - route.addAll(data.segments); + route.addAll(dataSegments); } } if (!locations.isEmpty() && !route.isEmpty()) { @@ -1156,14 +1091,8 @@ public class MeasurementEditingContext { return res; } - interface SnapToRoadProgressListener { - - void showProgressBar(); - - void updateProgress(int progress); - - void hideProgressBar(); - - void refresh(); + @Override + public void onRouteSettingsChanged(@Nullable ApplicationMode mode) { + recalculateRouteSegments(mode); } } diff --git a/OsmAnd/src/net/osmand/plus/measurementtool/MeasurementEditingContextUtils.java b/OsmAnd/src/net/osmand/plus/measurementtool/MeasurementEditingContextUtils.java new file mode 100644 index 0000000000..bd357429aa --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/measurementtool/MeasurementEditingContextUtils.java @@ -0,0 +1,67 @@ +package net.osmand.plus.measurementtool; + +import net.osmand.GPXUtilities; +import net.osmand.data.LatLon; +import net.osmand.router.RoutePlannerFrontEnd; +import net.osmand.router.RouteSegmentResult; +import net.osmand.util.MapUtils; + +import java.util.List; + +public class MeasurementEditingContextUtils { + + static boolean isLastGpxPoint(List gpxPoints, int index) { + if (index == gpxPoints.size() - 1) { + return true; + } else { + for (int i = index + 1; i < gpxPoints.size(); i++) { + RoutePlannerFrontEnd.GpxPoint gp = gpxPoints.get(i); + for (int k = 0; k < gp.routeToTarget.size(); k++) { + RouteSegmentResult seg = gp.routeToTarget.get(k); + if (seg.getStartPointIndex() != seg.getEndPointIndex()) { + return false; + } + } + + } + } + return true; + } + + static int findPointIndex(GPXUtilities.WptPt point, List points, int firstIndex) { + double minDistance = Double.MAX_VALUE; + int index = 0; + for (int i = Math.max(0, firstIndex); i < points.size(); i++) { + double distance = MapUtils.getDistance(point.lat, point.lon, points.get(i).lat, points.get(i).lon); + if (distance < minDistance) { + minDistance = distance; + index = i; + } + } + return index; + } + + static void addPointToArray(List points, RouteSegmentResult seg, int index, float[] heightArray) { + LatLon l = seg.getPoint(index); + GPXUtilities.WptPt pt = new GPXUtilities.WptPt(); + if (heightArray != null && heightArray.length > index * 2 + 1) { + pt.ele = heightArray[index * 2 + 1]; + } + pt.lat = l.getLatitude(); + pt.lon = l.getLongitude(); + points.add(pt); + } + + static void fillPointsArray(List points, RouteSegmentResult seg, boolean includeEndPoint) { + int ind = seg.getStartPointIndex(); + boolean plus = seg.isForwardDirection(); + float[] heightArray = seg.getObject().calculateHeightArray(); + while (ind != seg.getEndPointIndex()) { + addPointToArray(points, seg, ind, heightArray); + ind = plus ? ind + 1 : ind - 1; + } + if (includeEndPoint) { + addPointToArray(points, seg, ind, heightArray); + } + } +} diff --git a/OsmAnd/src/net/osmand/plus/measurementtool/MeasurementToolFragment.java b/OsmAnd/src/net/osmand/plus/measurementtool/MeasurementToolFragment.java index 3d8d9163b0..fe12a9212b 100644 --- a/OsmAnd/src/net/osmand/plus/measurementtool/MeasurementToolFragment.java +++ b/OsmAnd/src/net/osmand/plus/measurementtool/MeasurementToolFragment.java @@ -72,7 +72,6 @@ import net.osmand.plus.measurementtool.command.ReorderPointCommand; import net.osmand.plus.measurementtool.command.ReversePointsCommand; import net.osmand.plus.measurementtool.command.SplitPointsCommand; import net.osmand.plus.routepreparationmenu.RouteOptionsBottomSheet; -import net.osmand.plus.routepreparationmenu.RouteOptionsBottomSheet.OnAppModeConfiguredCallback; import net.osmand.plus.routepreparationmenu.RouteOptionsBottomSheet.DialogMode; import net.osmand.plus.routepreparationmenu.cards.BaseCard; import net.osmand.plus.settings.backend.ApplicationMode; @@ -98,7 +97,6 @@ import java.util.Locale; import static net.osmand.IndexConstants.GPX_FILE_EXT; import static net.osmand.IndexConstants.GPX_INDEX_DIR; import static net.osmand.plus.measurementtool.MeasurementEditingContext.CalculationMode; -import static net.osmand.plus.measurementtool.MeasurementEditingContext.SnapToRoadProgressListener; import static net.osmand.plus.measurementtool.SaveAsNewTrackBottomSheetDialogFragment.SaveAsNewTrackFragmentListener; import static net.osmand.plus.measurementtool.SelectFileBottomSheet.Mode.ADD_TO_TRACK; import static net.osmand.plus.measurementtool.SelectFileBottomSheet.SelectFileListener; @@ -109,8 +107,7 @@ import static net.osmand.plus.measurementtool.command.ClearPointsCommand.ClearCo public class MeasurementToolFragment extends BaseOsmAndFragment implements RouteBetweenPointsFragmentListener, OptionsFragmentListener, GpxApproximationFragmentListener, SelectedPointFragmentListener, - SaveAsNewTrackFragmentListener, MapControlsThemeInfoProvider, - OnAppModeConfiguredCallback { + SaveAsNewTrackFragmentListener, MapControlsThemeInfoProvider { public static final String TAG = MeasurementToolFragment.class.getSimpleName(); public static final String TAPS_DISABLED_KEY = "taps_disabled_key"; @@ -241,7 +238,8 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route final MeasurementToolLayer measurementLayer = mapActivity.getMapLayers().getMeasurementToolLayer(); final OsmandApplication app = mapActivity.getMyApplication(); - editingCtx.setApplication(mapActivity.getMyApplication()); + app.setMeasurementEditingContext(editingCtx); + editingCtx.setApplication(app); editingCtx.setProgressListener(new SnapToRoadProgressListener() { @Override public void showProgressBar() { @@ -267,6 +265,7 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route updateDistancePointsText(); } }); + editingCtx.setupRouteSettingsListener(); measurementLayer.setEditingCtx(editingCtx); @@ -930,7 +929,7 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route if (isFollowTrackMode()) { mapActivity.getMapActions().setGPXRouteParams(gpx); app.getTargetPointsHelper().updateRouteAndRefresh(true); - app.getRoutingHelper().recalculateRouteDueToSettingsChange(); + app.getRoutingHelper().onSettingsChanged(true); } else { mapActivity.getMapActions().stopNavigationActionConfirm(null , new Runnable() { @Override @@ -1102,12 +1101,6 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route updateDistancePointsText(); } - @Override - public void onAppModeConfigured() { - editingCtx.recalculateRouteSegmentsForAppMode(); - updateDistancePointsText(); - } - @Override public void onChangeRouteTypeBefore() { MapActivity mapActivity = getMapActivity(); @@ -1910,6 +1903,7 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route private void dismiss(@NonNull MapActivity mapActivity, boolean clearContext) { try { + OsmandApplication app = mapActivity.getMyApplication(); if (clearContext) { editingCtx.clearSegments(); } @@ -1920,13 +1914,15 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route GpxData gpxData = editingCtx.getGpxData(); GPXFile gpx = gpxData != null ? gpxData.getGpxFile() : null; if (gpx != null) { - Intent newIntent = new Intent(mapActivity, mapActivity.getMyApplication().getAppCustomization().getTrackActivity()); + Intent newIntent = new Intent(mapActivity, app.getAppCustomization().getTrackActivity()); newIntent.putExtra(TrackActivity.TRACK_FILE_NAME, gpx.path); newIntent.putExtra(TrackActivity.OPEN_TRACKS_LIST, true); newIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); startActivity(newIntent); } } + editingCtx.resetRouteSettingsListener(); + app.setMeasurementEditingContext(null); mapActivity.getSupportFragmentManager().beginTransaction().remove(this).commitAllowingStateLoss(); } catch (Exception e) { // ignore diff --git a/OsmAnd/src/net/osmand/plus/measurementtool/RoadSegmentData.java b/OsmAnd/src/net/osmand/plus/measurementtool/RoadSegmentData.java new file mode 100644 index 0000000000..a51bbaf6e3 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/measurementtool/RoadSegmentData.java @@ -0,0 +1,68 @@ +package net.osmand.plus.measurementtool; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import net.osmand.GPXUtilities.WptPt; +import net.osmand.plus.settings.backend.ApplicationMode; +import net.osmand.router.RouteSegmentResult; +import net.osmand.util.MapUtils; + +import java.util.Collections; +import java.util.List; + +public class RoadSegmentData { + private final ApplicationMode appMode; + private final WptPt start; + private final WptPt end; + private final List points; + private final List segments; + private final double distance; + + public RoadSegmentData(@NonNull ApplicationMode appMode, @NonNull WptPt start, @NonNull WptPt end, + @Nullable List points, @Nullable List segments) { + this.appMode = appMode; + this.start = start; + this.end = end; + this.points = points; + this.segments = segments; + double distance = 0; + if (points != null && points.size() > 1) { + for (int i = 1; i < points.size(); i++) { + distance += MapUtils.getDistance(points.get(i - 1).lat, points.get(i - 1).lon, + points.get(i).lat, points.get(i).lon); + } + } else if (segments != null) { + for (RouteSegmentResult segment : segments) { + distance += segment.getDistance(); + } + } + this.distance = distance; + } + + public ApplicationMode getAppMode() { + return appMode; + } + + public WptPt getStart() { + return start; + } + + public WptPt getEnd() { + return end; + } + + @Nullable + public List getPoints() { + return points != null ? Collections.unmodifiableList(points) : null; + } + + @Nullable + public List getSegments() { + return segments != null ? Collections.unmodifiableList(segments) : null; + } + + public double getDistance() { + return distance; + } +} diff --git a/OsmAnd/src/net/osmand/plus/measurementtool/SnapToRoadProgressListener.java b/OsmAnd/src/net/osmand/plus/measurementtool/SnapToRoadProgressListener.java new file mode 100644 index 0000000000..0af3c58f6b --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/measurementtool/SnapToRoadProgressListener.java @@ -0,0 +1,12 @@ +package net.osmand.plus.measurementtool; + +interface SnapToRoadProgressListener { + + void showProgressBar(); + + void updateProgress(int progress); + + void hideProgressBar(); + + void refresh(); +} diff --git a/OsmAnd/src/net/osmand/plus/measurementtool/command/ApplyGpxApproximationCommand.java b/OsmAnd/src/net/osmand/plus/measurementtool/command/ApplyGpxApproximationCommand.java index 8685e475ac..d8d61d4357 100644 --- a/OsmAnd/src/net/osmand/plus/measurementtool/command/ApplyGpxApproximationCommand.java +++ b/OsmAnd/src/net/osmand/plus/measurementtool/command/ApplyGpxApproximationCommand.java @@ -6,7 +6,7 @@ import androidx.annotation.NonNull; import net.osmand.GPXUtilities.WptPt; import net.osmand.plus.measurementtool.MeasurementEditingContext; -import net.osmand.plus.measurementtool.MeasurementEditingContext.RoadSegmentData; +import net.osmand.plus.measurementtool.RoadSegmentData; import net.osmand.plus.measurementtool.MeasurementToolLayer; import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.router.RoutePlannerFrontEnd.GpxRouteApproximation; diff --git a/OsmAnd/src/net/osmand/plus/measurementtool/command/ChangeRouteModeCommand.java b/OsmAnd/src/net/osmand/plus/measurementtool/command/ChangeRouteModeCommand.java index a9cfa2bb1e..36525b9d6b 100644 --- a/OsmAnd/src/net/osmand/plus/measurementtool/command/ChangeRouteModeCommand.java +++ b/OsmAnd/src/net/osmand/plus/measurementtool/command/ChangeRouteModeCommand.java @@ -4,7 +4,7 @@ import android.util.Pair; import net.osmand.GPXUtilities.WptPt; import net.osmand.plus.measurementtool.MeasurementEditingContext; -import net.osmand.plus.measurementtool.MeasurementEditingContext.RoadSegmentData; +import net.osmand.plus.measurementtool.RoadSegmentData; import net.osmand.plus.measurementtool.MeasurementToolLayer; import net.osmand.plus.settings.backend.ApplicationMode; diff --git a/OsmAnd/src/net/osmand/plus/measurementtool/command/ClearPointsCommand.java b/OsmAnd/src/net/osmand/plus/measurementtool/command/ClearPointsCommand.java index 14533a150d..83bf3ec15c 100644 --- a/OsmAnd/src/net/osmand/plus/measurementtool/command/ClearPointsCommand.java +++ b/OsmAnd/src/net/osmand/plus/measurementtool/command/ClearPointsCommand.java @@ -4,7 +4,7 @@ import android.util.Pair; import net.osmand.GPXUtilities.WptPt; import net.osmand.plus.measurementtool.MeasurementEditingContext; -import net.osmand.plus.measurementtool.MeasurementEditingContext.RoadSegmentData; +import net.osmand.plus.measurementtool.RoadSegmentData; import net.osmand.plus.measurementtool.MeasurementToolLayer; import java.util.ArrayList; diff --git a/OsmAnd/src/net/osmand/plus/measurementtool/command/JoinPointsCommand.java b/OsmAnd/src/net/osmand/plus/measurementtool/command/JoinPointsCommand.java index 95a311a9d3..97dd10e53d 100644 --- a/OsmAnd/src/net/osmand/plus/measurementtool/command/JoinPointsCommand.java +++ b/OsmAnd/src/net/osmand/plus/measurementtool/command/JoinPointsCommand.java @@ -4,7 +4,7 @@ import android.util.Pair; import net.osmand.GPXUtilities.WptPt; import net.osmand.plus.measurementtool.MeasurementEditingContext; -import net.osmand.plus.measurementtool.MeasurementEditingContext.RoadSegmentData; +import net.osmand.plus.measurementtool.RoadSegmentData; import net.osmand.plus.measurementtool.MeasurementToolLayer; import java.util.ArrayList; diff --git a/OsmAnd/src/net/osmand/plus/measurementtool/command/ReversePointsCommand.java b/OsmAnd/src/net/osmand/plus/measurementtool/command/ReversePointsCommand.java index 3c17998f3c..13be0cd4f9 100644 --- a/OsmAnd/src/net/osmand/plus/measurementtool/command/ReversePointsCommand.java +++ b/OsmAnd/src/net/osmand/plus/measurementtool/command/ReversePointsCommand.java @@ -4,12 +4,11 @@ import android.util.Pair; import net.osmand.GPXUtilities.WptPt; import net.osmand.plus.measurementtool.MeasurementEditingContext; -import net.osmand.plus.measurementtool.MeasurementEditingContext.RoadSegmentData; +import net.osmand.plus.measurementtool.RoadSegmentData; import net.osmand.plus.measurementtool.MeasurementToolLayer; import net.osmand.plus.settings.backend.ApplicationMode; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Map; diff --git a/OsmAnd/src/net/osmand/plus/measurementtool/command/SplitPointsCommand.java b/OsmAnd/src/net/osmand/plus/measurementtool/command/SplitPointsCommand.java index 6bfb75d682..21edf27e81 100644 --- a/OsmAnd/src/net/osmand/plus/measurementtool/command/SplitPointsCommand.java +++ b/OsmAnd/src/net/osmand/plus/measurementtool/command/SplitPointsCommand.java @@ -2,11 +2,9 @@ package net.osmand.plus.measurementtool.command; import android.util.Pair; -import net.osmand.GPXUtilities; -import net.osmand.GPXUtilities.TrkSegment; import net.osmand.GPXUtilities.WptPt; import net.osmand.plus.measurementtool.MeasurementEditingContext; -import net.osmand.plus.measurementtool.MeasurementEditingContext.RoadSegmentData; +import net.osmand.plus.measurementtool.RoadSegmentData; import net.osmand.plus.measurementtool.MeasurementToolLayer; import java.util.ArrayList; diff --git a/OsmAnd/src/net/osmand/plus/profiles/ConfigureProfileMenuAdapter.java b/OsmAnd/src/net/osmand/plus/profiles/ConfigureProfileMenuAdapter.java index 23189180a0..a2e024f860 100644 --- a/OsmAnd/src/net/osmand/plus/profiles/ConfigureProfileMenuAdapter.java +++ b/OsmAnd/src/net/osmand/plus/profiles/ConfigureProfileMenuAdapter.java @@ -16,7 +16,6 @@ import net.osmand.PlatformUtil; import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; -import net.osmand.plus.settings.fragments.BaseSettingsFragment; import net.osmand.util.Algorithms; import org.apache.commons.logging.Log; @@ -105,7 +104,7 @@ public class ConfigureProfileMenuAdapter extends AbstractProfileMenuAdapter { private String name; @@ -62,4 +63,5 @@ public class ProfileDataObject implements Comparable { public int compareTo(@NonNull ProfileDataObject another) { return this.name.compareToIgnoreCase(another.name); } + } diff --git a/OsmAnd/src/net/osmand/plus/profiles/ProfileDataUtils.java b/OsmAnd/src/net/osmand/plus/profiles/ProfileDataUtils.java new file mode 100644 index 0000000000..4cbc9b1940 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/profiles/ProfileDataUtils.java @@ -0,0 +1,136 @@ +package net.osmand.plus.profiles; + +import android.content.Context; + +import net.osmand.plus.OsmandApplication; +import net.osmand.plus.OsmandPlugin; +import net.osmand.plus.R; +import net.osmand.plus.settings.backend.ApplicationMode; +import net.osmand.router.GeneralRouter; +import net.osmand.router.RoutingConfiguration; +import net.osmand.util.Algorithms; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class ProfileDataUtils { + + public static final String OSMAND_NAVIGATION = "osmand_navigation"; + + public static List getDataObjects(OsmandApplication app, + List appModes) { + List profiles = new ArrayList<>(); + for (ApplicationMode mode : appModes) { + String description = mode.getDescription(); + if (Algorithms.isEmpty(description)) { + description = getAppModeDescription(app, mode); + } + profiles.add(new ProfileDataObject(mode.toHumanString(), description, + mode.getStringKey(), mode.getIconRes(), false, mode.getIconColorInfo())); + } + return profiles; + } + + public static String getAppModeDescription(Context ctx, ApplicationMode mode) { + String description; + if (mode.isCustomProfile()) { + description = ctx.getString(R.string.profile_type_user_string); + } else { + description = ctx.getString(R.string.profile_type_osmand_string); + } + + return description; + } + + public static List getSortedRoutingProfiles(OsmandApplication app) { + List result = new ArrayList<>(); + Map> routingProfilesByFileNames = getRoutingProfilesByFileNames(app); + List fileNames = new ArrayList<>(routingProfilesByFileNames.keySet()); + Collections.sort(fileNames, new Comparator() { + @Override + public int compare(String s, String t1) { + return s.equals(OSMAND_NAVIGATION) ? -1 : t1.equals(OSMAND_NAVIGATION) ? 1 : s.compareToIgnoreCase(t1); + } + }); + for (String fileName : fileNames) { + List routingProfilesFromFile = routingProfilesByFileNames.get(fileName); + if (routingProfilesFromFile != null) { + Collections.sort(routingProfilesFromFile); + result.addAll(routingProfilesFromFile); + } + } + return result; + } + + public static Map> getRoutingProfilesByFileNames(OsmandApplication app) { + Map> result = new HashMap<>(); + for (final RoutingProfileDataObject profile : getRoutingProfiles(app).values()) { + String fileName = profile.getFileName() != null ? profile.getFileName() : OSMAND_NAVIGATION; + if (result.containsKey(fileName)) { + result.get(fileName).add(profile); + } else { + result.put(fileName, new ArrayList() { + { add(profile); } + }); + } + } + return result; + } + + public static Map getRoutingProfiles(OsmandApplication context) { + Map profilesObjects = new HashMap<>(); + profilesObjects.put(RoutingProfileDataObject.RoutingProfilesResources.STRAIGHT_LINE_MODE.name(), new RoutingProfileDataObject( + RoutingProfileDataObject.RoutingProfilesResources.STRAIGHT_LINE_MODE.name(), + context.getString(RoutingProfileDataObject.RoutingProfilesResources.STRAIGHT_LINE_MODE.getStringRes()), + context.getString(R.string.special_routing_type), + RoutingProfileDataObject.RoutingProfilesResources.STRAIGHT_LINE_MODE.getIconRes(), + false, null)); + profilesObjects.put(RoutingProfileDataObject.RoutingProfilesResources.DIRECT_TO_MODE.name(), new RoutingProfileDataObject( + RoutingProfileDataObject.RoutingProfilesResources.DIRECT_TO_MODE.name(), + context.getString(RoutingProfileDataObject.RoutingProfilesResources.DIRECT_TO_MODE.getStringRes()), + context.getString(R.string.special_routing_type), + RoutingProfileDataObject.RoutingProfilesResources.DIRECT_TO_MODE.getIconRes(), + false, null)); + if (context.getBRouterService() != null) { + profilesObjects.put(RoutingProfileDataObject.RoutingProfilesResources.BROUTER_MODE.name(), new RoutingProfileDataObject( + RoutingProfileDataObject.RoutingProfilesResources.BROUTER_MODE.name(), + context.getString(RoutingProfileDataObject.RoutingProfilesResources.BROUTER_MODE.getStringRes()), + context.getString(R.string.third_party_routing_type), + RoutingProfileDataObject.RoutingProfilesResources.BROUTER_MODE.getIconRes(), + false, null)); + } + + List disabledRouterNames = OsmandPlugin.getDisabledRouterNames(); + for (RoutingConfiguration.Builder builder : context.getAllRoutingConfigs()) { + collectRoutingProfilesFromConfig(context, builder, profilesObjects, disabledRouterNames); + } + return profilesObjects; + } + + private static void collectRoutingProfilesFromConfig(OsmandApplication app, RoutingConfiguration.Builder builder, + Map profilesObjects, List disabledRouterNames) { + for (Map.Entry entry : builder.getAllRouters().entrySet()) { + String routerKey = entry.getKey(); + GeneralRouter router = entry.getValue(); + if (!routerKey.equals("geocoding") && !disabledRouterNames.contains(router.getFilename())) { + int iconRes = R.drawable.ic_action_gdirections_dark; + String name = router.getProfileName(); + String description = app.getString(R.string.osmand_default_routing); + String fileName = router.getFilename(); + if (!Algorithms.isEmpty(fileName)) { + description = fileName; + } else if (RoutingProfileDataObject.RoutingProfilesResources.isRpValue(name.toUpperCase())) { + iconRes = RoutingProfileDataObject.RoutingProfilesResources.valueOf(name.toUpperCase()).getIconRes(); + name = app.getString(RoutingProfileDataObject.RoutingProfilesResources.valueOf(name.toUpperCase()).getStringRes()); + } + profilesObjects.put(routerKey, new RoutingProfileDataObject(routerKey, name, description, + iconRes, false, fileName)); + } + } + } + +} diff --git a/OsmAnd/src/net/osmand/plus/profiles/RoutingProfileDataObject.java b/OsmAnd/src/net/osmand/plus/profiles/RoutingProfileDataObject.java index 3cd3c74293..ab808de360 100644 --- a/OsmAnd/src/net/osmand/plus/profiles/RoutingProfileDataObject.java +++ b/OsmAnd/src/net/osmand/plus/profiles/RoutingProfileDataObject.java @@ -59,4 +59,5 @@ public class RoutingProfileDataObject extends ProfileDataObject { return rpValues.contains(value); } } + } diff --git a/OsmAnd/src/net/osmand/plus/profiles/SelectAppModesBottomSheetDialogFragment.java b/OsmAnd/src/net/osmand/plus/profiles/SelectAppModesBottomSheetDialogFragment.java index c7a49a7ab5..825bc61b04 100644 --- a/OsmAnd/src/net/osmand/plus/profiles/SelectAppModesBottomSheetDialogFragment.java +++ b/OsmAnd/src/net/osmand/plus/profiles/SelectAppModesBottomSheetDialogFragment.java @@ -88,7 +88,7 @@ public class SelectAppModesBottomSheetDialogFragment extends AppModesBottomSheet OsmandSettings settings = getMyApplication().getSettings(); if (appMode != this.appMode) { if (appModeChangeable) { - settings.APPLICATION_MODE.set(appMode); + settings.setApplicationMode(appMode); } Fragment targetFragment = getTargetFragment(); if (targetFragment instanceof AppModeChangedListener) { diff --git a/OsmAnd/src/net/osmand/plus/profiles/SelectMultipleProfilesBottomSheet.java b/OsmAnd/src/net/osmand/plus/profiles/SelectMultipleProfilesBottomSheet.java index 23068084d8..7687f3386b 100644 --- a/OsmAnd/src/net/osmand/plus/profiles/SelectMultipleProfilesBottomSheet.java +++ b/OsmAnd/src/net/osmand/plus/profiles/SelectMultipleProfilesBottomSheet.java @@ -19,7 +19,7 @@ import net.osmand.plus.UiUtilities; import net.osmand.plus.activities.MapActivity; import net.osmand.plus.base.bottomsheetmenu.BaseBottomSheetItem; import net.osmand.plus.base.bottomsheetmenu.simpleitems.TitleItem; -import net.osmand.plus.settings.fragments.NavigationFragment; +import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.settings.bottomsheets.BasePreferenceBottomSheet; import java.util.ArrayList; @@ -54,7 +54,8 @@ public class SelectMultipleProfilesBottomSheet extends BasePreferenceBottomSheet private void refreshProfiles(OsmandApplication app) { profiles.clear(); - profiles.addAll(NavigationFragment.getBaseProfiles(app, true)); + List appModes = ApplicationMode.allPossibleValues(); + profiles.addAll(ProfileDataUtils.getDataObjects(app, appModes)); for (ProfileDataObject profile : profiles) { String key = profile.getStringKey(); profile.setSelected(selectedProfiles.contains(key)); diff --git a/OsmAnd/src/net/osmand/plus/profiles/SelectProfileBottomSheetDialogFragment.java b/OsmAnd/src/net/osmand/plus/profiles/SelectProfileBottomSheet.java similarity index 62% rename from OsmAnd/src/net/osmand/plus/profiles/SelectProfileBottomSheetDialogFragment.java rename to OsmAnd/src/net/osmand/plus/profiles/SelectProfileBottomSheet.java index 743c9897ad..eaf99b9503 100644 --- a/OsmAnd/src/net/osmand/plus/profiles/SelectProfileBottomSheetDialogFragment.java +++ b/OsmAnd/src/net/osmand/plus/profiles/SelectProfileBottomSheet.java @@ -2,8 +2,12 @@ package net.osmand.plus.profiles; import android.app.Activity; import android.content.Context; +import android.graphics.Typeface; import android.graphics.drawable.Drawable; import android.os.Bundle; +import android.text.SpannableString; +import android.text.Spanned; +import android.text.style.ForegroundColorSpan; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; @@ -15,6 +19,8 @@ import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.core.content.ContextCompat; +import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentManager; @@ -28,10 +34,10 @@ import net.osmand.plus.activities.MapActivity; import net.osmand.plus.base.bottomsheetmenu.BaseBottomSheetItem; import net.osmand.plus.base.bottomsheetmenu.simpleitems.DividerItem; import net.osmand.plus.base.bottomsheetmenu.simpleitems.LongDescriptionItem; +import net.osmand.plus.base.bottomsheetmenu.simpleitems.SimpleDividerItem; import net.osmand.plus.base.bottomsheetmenu.simpleitems.TitleItem; -import net.osmand.plus.settings.fragments.MainSettingsFragment; -import net.osmand.plus.settings.fragments.NavigationFragment; -import net.osmand.plus.settings.fragments.ProfileAppearanceFragment; +import net.osmand.plus.helpers.FontCache; +import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.settings.bottomsheets.BasePreferenceBottomSheet; import net.osmand.router.RoutingConfiguration; @@ -42,35 +48,36 @@ import java.util.List; import static net.osmand.plus.importfiles.ImportHelper.ImportType.ROUTING; -public class SelectProfileBottomSheetDialogFragment extends BasePreferenceBottomSheet { +public class SelectProfileBottomSheet extends BasePreferenceBottomSheet { - private static final Log LOG = PlatformUtil - .getLog(SelectProfileBottomSheetDialogFragment.class); - public static final String TAG = "SelectProfileBottomSheetDialogFragment"; + private static final Log LOG = PlatformUtil.getLog(SelectProfileBottomSheet.class); + public static final String TAG = "SelectProfileBottomSheet"; public final static String DIALOG_TYPE = "dialog_type"; - public final static String TYPE_BASE_APP_PROFILE = "base_profiles"; - public final static String TYPE_NAV_PROFILE = "routing_profiles"; public final static String SELECTED_KEY = "selected_base"; public final static String PROFILE_KEY_ARG = "profile_key_arg"; + public final static String USE_LAST_PROFILE_ARG = "use_last_profile_arg"; public final static String IS_PROFILE_IMPORTED_ARG = "is_profile_imported_arg"; - String type; - - private SelectProfileListener listener; - + private DialogMode dialogMode; private final List profiles = new ArrayList<>(); - private String selectedItemKey; + public enum DialogMode { + BASE_PROFILE, + NAVIGATION_PROFILE, + DEFAULT_PROFILE + } + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); OsmandApplication app = getMyApplication(); Bundle args = getArguments(); if (args != null && args.get(DIALOG_TYPE) != null) { - type = args.getString(DIALOG_TYPE); + String dialogModeName = args.getString(DIALOG_TYPE); + dialogMode = DialogMode.valueOf(dialogModeName); selectedItemKey = args.getString(SELECTED_KEY, null); refreshProfiles(app); } @@ -91,12 +98,12 @@ public class SelectProfileBottomSheetDialogFragment extends BasePreferenceBottom @Override public void createMenuItems(Bundle savedInstanceState) { OsmandApplication app = requiredMyApplication(); - + View bottomSpaceView = new View(app); int space = (int) getResources().getDimension(R.dimen.empty_state_text_button_padding_top); bottomSpaceView.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, space)); - - if (type.equals(TYPE_BASE_APP_PROFILE)) { + + if (dialogMode == DialogMode.BASE_PROFILE) { items.add(new TitleItem(getString(R.string.select_base_profile_dialog_title))); items.add(new LongDescriptionItem(getString(R.string.select_base_profile_dialog_message))); for (int i = 0; i < profiles.size(); i++) { @@ -138,7 +145,7 @@ public class SelectProfileBottomSheetDialogFragment extends BasePreferenceBottom .setCustomView(bottomSpaceView) .create());*/ - } else if (type.equals(TYPE_NAV_PROFILE)) { + } else if (dialogMode == DialogMode.NAVIGATION_PROFILE) { items.add(new TitleItem(getString(R.string.select_nav_profile_dialog_title))); items.add(new LongDescriptionItem(getString(R.string.select_nav_profile_dialog_message))); for (int i = 0; i < profiles.size(); i++) { @@ -146,11 +153,11 @@ public class SelectProfileBottomSheetDialogFragment extends BasePreferenceBottom boolean showBottomDivider = false; if (i < profiles.size() - 1) { RoutingProfileDataObject nextProfile = (RoutingProfileDataObject) profiles.get(i + 1); - if (profile.getFileName() == null) { - showBottomDivider = nextProfile.getFileName() != null; - } else { - showBottomDivider = !profile.getFileName().equals(nextProfile.getFileName()); - } + if (profile.getFileName() == null) { + showBottomDivider = nextProfile.getFileName() != null; + } else { + showBottomDivider = !profile.getFileName().equals(nextProfile.getFileName()); + } } addProfileItem(profile, showBottomDivider); } @@ -175,15 +182,44 @@ public class SelectProfileBottomSheetDialogFragment extends BasePreferenceBottom items.add(new BaseBottomSheetItem.Builder() .setCustomView(bottomSpaceView) .create()); + } else if (dialogMode == DialogMode.DEFAULT_PROFILE) { + items.add(new TitleItem(getString(R.string.settings_preset))); + items.add(new LongDescriptionItem(getString(R.string.profile_by_default_description))); + + boolean useLastAppModeByDefault = app.getSettings().USE_LAST_APPLICATION_MODE_BY_DEFAULT.get(); + addCheckableItem(R.string.shared_string_last_used, useLastAppModeByDefault, new OnClickListener() { + @Override + public void onClick(View v) { + Bundle args = new Bundle(); + args.putBoolean(USE_LAST_PROFILE_ARG, true); + Fragment target = getTargetFragment(); + if (target instanceof OnSelectProfileCallback) { + ((OnSelectProfileCallback) target).onProfileSelected(args); + } + dismiss(); + } + }); + + items.add(new SimpleDividerItem(app)); + for (int i = 0; i < profiles.size(); i++) { + ProfileDataObject profile = profiles.get(i); + addProfileItem(profile, false, !useLastAppModeByDefault); + } } } - + private void addProfileItem(final ProfileDataObject profile, boolean showBottomDivider) { + addProfileItem(profile, showBottomDivider, true); + } + + private void addProfileItem(final ProfileDataObject profile, + boolean showBottomDivider, + boolean setupSelected) { OsmandApplication app = requiredMyApplication(); - + int activeColorResId = nightMode ? R.color.active_color_primary_dark : R.color.active_color_primary_light; int iconDefaultColorResId = nightMode ? R.color.icon_color_default_dark : R.color.icon_color_default_light; - + View itemView = UiUtilities.getInflater(getContext(), nightMode).inflate(R.layout.bottom_sheet_item_with_descr_and_radio_btn, null); TextView tvTitle = itemView.findViewById(R.id.title); TextView tvDescription = itemView.findViewById(R.id.description); @@ -193,13 +229,13 @@ public class SelectProfileBottomSheetDialogFragment extends BasePreferenceBottom tvTitle.setText(profile.getName()); tvDescription.setText(profile.getDescription()); - - boolean isSelected = profile.getStringKey().equals(selectedItemKey); + + boolean isSelected = setupSelected && profile.getStringKey().equals(selectedItemKey); int iconColor; - if (type.equals(TYPE_BASE_APP_PROFILE)) { - iconColor = profile.getIconColor(nightMode); - } else { + if (dialogMode == DialogMode.NAVIGATION_PROFILE) { iconColor = isSelected ? activeColorResId : iconDefaultColorResId; + } else { + iconColor = profile.getIconColor(nightMode); } Drawable drawableIcon = app.getUIUtilities().getIcon(profile.getIconRes(), iconColor); @@ -213,28 +249,56 @@ public class SelectProfileBottomSheetDialogFragment extends BasePreferenceBottom .setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { - if (listener == null) { - getListener(); - } Bundle args = new Bundle(); args.putString(PROFILE_KEY_ARG, profile.getStringKey()); - listener.onSelectedType(args); + Fragment target = getTargetFragment(); + if (target instanceof OnSelectProfileCallback) { + ((OnSelectProfileCallback) target).onProfileSelected(args); + } dismiss(); } }) .create()); } - + + private void addCheckableItem(int titleId, + boolean isSelected, + OnClickListener listener) { + OsmandApplication app = requiredMyApplication(); + View itemView = UiUtilities.getInflater(getContext(), nightMode).inflate(R.layout.bottom_sheet_item_with_descr_and_radio_btn, null); + + itemView.findViewById(R.id.icon).setVisibility(View.GONE); + itemView.findViewById(R.id.description).setVisibility(View.GONE); + itemView.findViewById(R.id.divider_bottom).setVisibility(View.GONE); + + Typeface typeface = FontCache.getRobotoMedium(app); + String title = getString(titleId); + SpannableString spannable = UiUtilities.createCustomFontSpannable(typeface, title, title, title); + int activeColor = ContextCompat.getColor(app, getActiveColorId()); + ForegroundColorSpan colorSpan = new ForegroundColorSpan(activeColor); + spannable.setSpan(colorSpan, 0, title.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + ((TextView) itemView.findViewById(R.id.title)).setText(spannable); + + CompoundButton compoundButton = itemView.findViewById(R.id.compound_button); + compoundButton.setChecked(isSelected); + UiUtilities.setupCompoundButton(compoundButton, nightMode, UiUtilities.CompoundButtonType.GLOBAL); + + items.add(new BaseBottomSheetItem.Builder() + .setCustomView(itemView) + .setOnClickListener(listener) + .create()); + } + private void addButtonItem(int titleId, int iconId, OnClickListener listener) { OsmandApplication app = requiredMyApplication(); Context themedCtx = UiUtilities.getThemedContext(app, nightMode); - + int activeColorResId = AndroidUtils.resolveAttribute(themedCtx, R.attr.active_color_basic); - + View buttonView = View.inflate(themedCtx, R.layout.bottom_sheet_item_preference_btn, null); TextView tvTitle = buttonView.findViewById(R.id.title); - tvTitle.setText(app.getString(titleId)); - + tvTitle.setText(getString(titleId)); + ImageView ivIcon = buttonView.findViewById(R.id.icon); ivIcon.setImageDrawable(app.getUIUtilities().getIcon(iconId, activeColorResId)); @@ -244,18 +308,6 @@ public class SelectProfileBottomSheetDialogFragment extends BasePreferenceBottom .create()); } - private void refreshProfiles(OsmandApplication app) { - profiles.clear(); - if (type.equals(TYPE_NAV_PROFILE)) { - profiles.addAll(NavigationFragment.getSortedRoutingProfiles(app)); - } else if (type.equals(TYPE_BASE_APP_PROFILE)) { - profiles.addAll(NavigationFragment.getBaseProfiles(app)); - } else { - LOG.error("Check data type!"); - dismiss(); - } - } - private void refreshView() { Activity activity = getActivity(); View mainView = getView(); @@ -275,28 +327,25 @@ public class SelectProfileBottomSheetDialogFragment extends BasePreferenceBottom } } - private void getListener() { - FragmentActivity activity = getActivity(); - if (activity != null) { - FragmentManager fragmentManager = activity.getSupportFragmentManager(); - NavigationFragment navigationFragment = (NavigationFragment) fragmentManager.findFragmentByTag(NavigationFragment.class.getName()); - ProfileAppearanceFragment profileAppearanceFragment = (ProfileAppearanceFragment) fragmentManager.findFragmentByTag(ProfileAppearanceFragment.TAG); - MainSettingsFragment mainSettingsFragment = (MainSettingsFragment) fragmentManager.findFragmentByTag(MainSettingsFragment.TAG); + private void refreshProfiles(OsmandApplication app) { + profiles.clear(); + switch (dialogMode) { + case BASE_PROFILE: + List appModes = new ArrayList<>(ApplicationMode.allPossibleValues()); + appModes.remove(ApplicationMode.DEFAULT); + profiles.addAll(ProfileDataUtils.getDataObjects(app, appModes)); + break; - if (navigationFragment != null) { - listener = navigationFragment.getNavProfileListener(); - } else if (profileAppearanceFragment != null) { - listener = profileAppearanceFragment.getParentProfileListener(); - } else if (mainSettingsFragment != null) { - listener = mainSettingsFragment.getParentProfileListener(); - } + case NAVIGATION_PROFILE: + profiles.addAll(ProfileDataUtils.getSortedRoutingProfiles(app)); + break; + + case DEFAULT_PROFILE: + profiles.addAll(ProfileDataUtils.getDataObjects(app, ApplicationMode.values(app))); + break; } } - public interface SelectProfileListener { - void onSelectedType(Bundle args); - } - @Nullable private MapActivity getMapActivity() { Activity activity = getActivity(); @@ -305,4 +354,23 @@ public class SelectProfileBottomSheetDialogFragment extends BasePreferenceBottom } return null; } + + public static void showInstance(@NonNull FragmentActivity activity, + @NonNull DialogMode dialogMode, + @Nullable Fragment target, + String selectedItemKey, + boolean usedOnMap) { + SelectProfileBottomSheet fragment = new SelectProfileBottomSheet(); + Bundle args = new Bundle(); + args.putString(DIALOG_TYPE, dialogMode.name()); + args.putString(SELECTED_KEY, selectedItemKey); + fragment.setArguments(args); + fragment.setUsedOnMap(usedOnMap); + fragment.setTargetFragment(target, 0); + fragment.show(activity.getSupportFragmentManager(), TAG); + } + + public interface OnSelectProfileCallback { + void onProfileSelected(Bundle args); + } } diff --git a/OsmAnd/src/net/osmand/plus/profiles/SelectProfileMenuAdapter.java b/OsmAnd/src/net/osmand/plus/profiles/SelectProfileMenuAdapter.java index 684479fc6f..205f608c9a 100644 --- a/OsmAnd/src/net/osmand/plus/profiles/SelectProfileMenuAdapter.java +++ b/OsmAnd/src/net/osmand/plus/profiles/SelectProfileMenuAdapter.java @@ -19,7 +19,6 @@ import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; import net.osmand.plus.UiUtilities; -import net.osmand.plus.settings.fragments.BaseSettingsFragment; import net.osmand.util.Algorithms; import org.apache.commons.logging.Log; @@ -99,7 +98,7 @@ public class SelectProfileMenuAdapter extends AbstractProfileMenuAdapter { ApplicationMode appMode = getModeForKey(params); if (appMode != null) { OsmandApplication app = activity.getMyApplication(); - app.getSettings().APPLICATION_MODE.set(appMode); + app.getSettings().setApplicationMode(appMode); app.getQuickActionRegistry().setQuickActionFabState(true); String message = String.format(activity.getString( diff --git a/OsmAnd/src/net/osmand/plus/routepreparationmenu/AvoidRoadsBottomSheetDialogFragment.java b/OsmAnd/src/net/osmand/plus/routepreparationmenu/AvoidRoadsBottomSheetDialogFragment.java index ffcf3a5149..38f6053887 100644 --- a/OsmAnd/src/net/osmand/plus/routepreparationmenu/AvoidRoadsBottomSheetDialogFragment.java +++ b/OsmAnd/src/net/osmand/plus/routepreparationmenu/AvoidRoadsBottomSheetDialogFragment.java @@ -20,6 +20,7 @@ import androidx.fragment.app.Fragment; import net.osmand.AndroidUtils; import net.osmand.data.LatLon; import net.osmand.plus.OsmandApplication; +import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.settings.backend.CommonPreference; import net.osmand.plus.R; import net.osmand.plus.UiUtilities; @@ -32,6 +33,7 @@ import net.osmand.plus.base.bottomsheetmenu.simpleitems.SubtitleDividerItem; import net.osmand.plus.dashboard.DashboardOnMap; import net.osmand.plus.helpers.AvoidSpecificRoads; import net.osmand.router.GeneralRouter; +import net.osmand.router.RouteSegmentResult; import java.io.Serializable; import java.util.ArrayList; @@ -52,6 +54,7 @@ public class AvoidRoadsBottomSheetDialogFragment extends MenuBottomSheetDialogFr private static final String AVOID_ROADS_TYPES_KEY = "avoid_roads_types"; private static final String HIDE_IMPASSABLE_ROADS_KEY = "hide_impassable_roads"; private static final String AVOID_ROADS_OBJECTS_KEY = "avoid_roads_objects"; + private static final String AVOID_ROADS_APP_MODE_KEY = "avoid_roads_app_mode"; private RoutingOptionsHelper routingOptionsHelper; @@ -63,11 +66,16 @@ public class AvoidRoadsBottomSheetDialogFragment extends MenuBottomSheetDialogFr private boolean hideImpassableRoads; @ColorRes private int compoundButtonColorId = INVALID_ID; + private ApplicationMode appMode; public void setHideImpassableRoads(boolean hideImpassableRoads) { this.hideImpassableRoads = hideImpassableRoads; } + public void setApplicationMode(ApplicationMode appMode) { + this.appMode = appMode; + } + @Override public void createMenuItems(Bundle savedInstanceState) { final OsmandApplication app = getMyApplication(); @@ -83,6 +91,9 @@ public class AvoidRoadsBottomSheetDialogFragment extends MenuBottomSheetDialogFr if (savedInstanceState.containsKey(AVOID_ROADS_OBJECTS_KEY)) { removedImpassableRoads = (List) savedInstanceState.getSerializable(AVOID_ROADS_OBJECTS_KEY); } + if (savedInstanceState.containsKey(AVOID_ROADS_APP_MODE_KEY)) { + appMode = ApplicationMode.valueOfStringKey(savedInstanceState.getString(AVOID_ROADS_APP_MODE_KEY), null); + } } if (routingParametersMap == null) { routingParametersMap = getRoutingParametersMap(app); @@ -154,7 +165,7 @@ public class AvoidRoadsBottomSheetDialogFragment extends MenuBottomSheetDialogFr if (mapActivity != null) { mapActivity.getDashboard().setDashboardVisibility(false, DashboardOnMap.DashboardType.ROUTE_PREFERENCES); mapActivity.getMapRouteInfoMenu().hide(); - app.getAvoidSpecificRoads().selectFromMap(mapActivity); + app.getAvoidSpecificRoads().selectFromMap(mapActivity, appMode); Fragment fragment = getTargetFragment(); if (fragment != null) { fragment.onActivityResult(getTargetRequestCode(), OPEN_AVOID_ROADS_DIALOG_REQUEST_CODE, null); @@ -262,6 +273,9 @@ public class AvoidRoadsBottomSheetDialogFragment extends MenuBottomSheetDialogFr outState.putSerializable(AVOID_ROADS_TYPES_KEY, routingParametersMap); outState.putSerializable(AVOID_ROADS_OBJECTS_KEY, (Serializable) removedImpassableRoads); outState.putBoolean(HIDE_IMPASSABLE_ROADS_KEY, hideImpassableRoads); + if (appMode != null) { + outState.putString(AVOID_ROADS_APP_MODE_KEY, appMode.getStringKey()); + } } @Override @@ -301,7 +315,7 @@ public class AvoidRoadsBottomSheetDialogFragment extends MenuBottomSheetDialogFr avoidSpecificRoads.removeImpassableRoad(routeLocation); } - app.getRoutingHelper().recalculateRouteDueToSettingsChange(); + app.getRoutingHelper().onSettingsChanged(true); MapActivity mapActivity = getMapActivity(); if (mapActivity != null) { final MapRouteInfoMenu mapRouteInfoMenu = mapActivity.getMapRouteInfoMenu(); diff --git a/OsmAnd/src/net/osmand/plus/routepreparationmenu/FollowTrackFragment.java b/OsmAnd/src/net/osmand/plus/routepreparationmenu/FollowTrackFragment.java index f20b9f9350..e75dad04de 100644 --- a/OsmAnd/src/net/osmand/plus/routepreparationmenu/FollowTrackFragment.java +++ b/OsmAnd/src/net/osmand/plus/routepreparationmenu/FollowTrackFragment.java @@ -521,7 +521,7 @@ public class FollowTrackFragment extends ContextMenuScrollFragment implements Ca } mapActivity.getMapActions().setGPXRouteParams(gpxFile); app.getTargetPointsHelper().updateRouteAndRefresh(true); - app.getRoutingHelper().recalculateRouteDueToSettingsChange(); + app.getRoutingHelper().onSettingsChanged(true); } } diff --git a/OsmAnd/src/net/osmand/plus/routepreparationmenu/MapRouteInfoMenu.java b/OsmAnd/src/net/osmand/plus/routepreparationmenu/MapRouteInfoMenu.java index c856a47426..9903d1e77d 100644 --- a/OsmAnd/src/net/osmand/plus/routepreparationmenu/MapRouteInfoMenu.java +++ b/OsmAnd/src/net/osmand/plus/routepreparationmenu/MapRouteInfoMenu.java @@ -9,6 +9,7 @@ import android.os.Build; import android.os.Build.VERSION_CODES; import android.os.Bundle; import android.os.Handler; +import android.util.Pair; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; @@ -51,12 +52,8 @@ import net.osmand.plus.GeocodingLookupService; import net.osmand.plus.GeocodingLookupService.AddressLookupRequest; import net.osmand.plus.GeocodingLookupService.OnAddressLookupResult; import net.osmand.plus.GpxSelectionHelper.SelectedGpxFile; -import net.osmand.plus.mapmarkers.MapMarker; import net.osmand.plus.OsmAndLocationProvider; import net.osmand.plus.OsmandApplication; -import net.osmand.plus.settings.backend.OsmandSettings; -import net.osmand.plus.settings.backend.CommonPreference; -import net.osmand.plus.settings.backend.OsmandPreference; import net.osmand.plus.R; import net.osmand.plus.TargetPointsHelper; import net.osmand.plus.TargetPointsHelper.TargetPoint; @@ -70,6 +67,7 @@ import net.osmand.plus.helpers.AvoidSpecificRoads.AvoidRoadInfo; import net.osmand.plus.helpers.GpxUiHelper; import net.osmand.plus.helpers.WaypointHelper; import net.osmand.plus.mapcontextmenu.other.TrackDetailsMenuFragment; +import net.osmand.plus.mapmarkers.MapMarker; import net.osmand.plus.mapmarkers.MapMarkerSelectionFragment; import net.osmand.plus.poi.PoiUIFilter; import net.osmand.plus.profiles.AppModesBottomSheetDialogFragment; @@ -101,9 +99,16 @@ import net.osmand.plus.routing.IRouteInformationListener; import net.osmand.plus.routing.RouteCalculationResult; import net.osmand.plus.routing.RouteProvider.GPXRouteParamsBuilder; import net.osmand.plus.routing.RoutingHelper; +import net.osmand.plus.routing.RoutingHelperUtils; import net.osmand.plus.routing.TransportRoutingHelper; import net.osmand.plus.search.QuickSearchHelper; import net.osmand.plus.settings.backend.ApplicationMode; +import net.osmand.plus.settings.backend.CommonPreference; +import net.osmand.plus.settings.backend.OsmandPreference; +import net.osmand.plus.settings.backend.OsmandSettings; +import net.osmand.plus.views.OsmandMapLayer; +import net.osmand.plus.views.OsmandMapTileView; +import net.osmand.plus.views.layers.ContextMenuLayer.IContextMenuProvider; import net.osmand.plus.widgets.TextViewExProgress; import net.osmand.router.GeneralRouter; import net.osmand.router.GeneralRouter.RoutingParameter; @@ -281,7 +286,10 @@ public class MapRouteInfoMenu implements IRouteInformationListener, CardListener if (selectFromMapTouch) { selectFromMapTouch = false; LatLon latLon = tileBox.getLatLonFromPixel(point.x, point.y); - choosePointTypeAction(mapActivity, latLon, selectFromMapPointType, null, null); + Pair pair = getObjectLocation(mapActivity.getMapView(), point, tileBox); + LatLon selectedPoint = pair != null ? pair.first : latLon; + PointDescription name = pair != null ? pair.second : null; + choosePointTypeAction(mapActivity, selectedPoint, selectFromMapPointType, name, null); if (selectFromMapWaypoints) { WaypointsFragment.showInstance(mapActivity.getSupportFragmentManager(), true); } else { @@ -293,6 +301,28 @@ public class MapRouteInfoMenu implements IRouteInformationListener, CardListener return false; } + private Pair getObjectLocation(OsmandMapTileView mapView, PointF point, RotatedTileBox tileBox) { + List objects = new ArrayList<>(); + for (OsmandMapLayer layer : mapView.getLayers()) { + if (layer instanceof IContextMenuProvider) { + objects.clear(); + IContextMenuProvider provider = (IContextMenuProvider) layer; + provider.collectObjectsFromPoint(point, tileBox, objects, true); + for (Object o : objects) { + if (provider.isObjectClickable(o)) { + LatLon latLon = provider.getObjectLocation(o); + PointDescription name = null; + if (o instanceof FavouritePoint) { + name = ((FavouritePoint) o).getPointDescription(mapView.getApplication()); + } + return new Pair<>(latLon, name); + } + } + } + } + return null; + } + private void choosePointTypeAction(MapActivity mapActivity, LatLon latLon, PointType pointType, PointDescription pd, String address) { OsmandApplication app = getApp(); FavouritesDbHelper favorites = app.getFavorites(); @@ -862,7 +892,7 @@ public class MapRouteInfoMenu implements IRouteInformationListener, CardListener } routingHelper.setAppMode(next); app.initVoiceCommandPlayer(mapActivity, next, true, null, false, false, true); - routingHelper.recalculateRouteDueToSettingsChange(); + routingHelper.onSettingsChanged(true); } } @@ -1279,7 +1309,7 @@ public class MapRouteInfoMenu implements IRouteInformationListener, CardListener if (mapActivity != null) { OsmandApplication app = mapActivity.getMyApplication(); app.getAvoidSpecificRoads().removeImpassableRoad(avoidRoadInfo); - app.getRoutingHelper().recalculateRouteDueToSettingsChange(); + app.getRoutingHelper().onSettingsChanged(true); if (app.getAvoidSpecificRoads().getImpassableRoads().isEmpty() && getAvoidedParameters(app).isEmpty()) { mode.parameters.remove(parameter); } @@ -1312,7 +1342,7 @@ public class MapRouteInfoMenu implements IRouteInformationListener, CardListener CommonPreference preference = settings.getCustomRoutingBooleanProperty(routingParameter.getId(), routingParameter.getDefaultBoolean()); preference.setModeValue(app.getRoutingHelper().getAppMode(), false); avoidedParameters.remove(routingParameter); - app.getRoutingHelper().recalculateRouteDueToSettingsChange(); + app.getRoutingHelper().onSettingsChanged(true); if (app.getAvoidSpecificRoads().getImpassableRoads().isEmpty() && avoidedParameters.isEmpty()) { mode.parameters.remove(parameter); } @@ -2411,7 +2441,7 @@ public class MapRouteInfoMenu implements IRouteInformationListener, CardListener } } else if (routingHelper.isRouteCalculated()) { RouteCalculationResult result = routingHelper.getRoute(); - QuadRect routeRect = routingHelper.getRouteRect(result); + QuadRect routeRect = RoutingHelperUtils.getRouteRect(app, result); if (routeRect != null) { rect = routeRect; } diff --git a/OsmAnd/src/net/osmand/plus/routepreparationmenu/RouteOptionsBottomSheet.java b/OsmAnd/src/net/osmand/plus/routepreparationmenu/RouteOptionsBottomSheet.java index 8daf44e8e8..9916813c5e 100644 --- a/OsmAnd/src/net/osmand/plus/routepreparationmenu/RouteOptionsBottomSheet.java +++ b/OsmAnd/src/net/osmand/plus/routepreparationmenu/RouteOptionsBottomSheet.java @@ -1,7 +1,6 @@ package net.osmand.plus.routepreparationmenu; import android.app.Activity; -import android.content.DialogInterface; import android.content.Intent; import android.graphics.drawable.Drawable; import android.os.Build; @@ -13,7 +12,6 @@ import android.widget.LinearLayout; import android.widget.TextView; import androidx.annotation.ColorRes; -import androidx.annotation.NonNull; import androidx.appcompat.widget.SwitchCompat; import androidx.core.content.ContextCompat; import androidx.fragment.app.Fragment; @@ -400,7 +398,7 @@ public class RouteOptionsBottomSheet extends MenuBottomSheetDialogFragment { boolean enabled = !settings.ENABLE_TIME_CONDITIONAL_ROUTING.getModeValue(applicationMode); settings.ENABLE_TIME_CONDITIONAL_ROUTING.setModeValue(applicationMode, enabled); timeConditionalRoutingItem[0].setChecked(enabled); - app.getRoutingHelper().recalculateRouteDueToSettingsChange(); + app.getRoutingHelper().onSettingsChanged(applicationMode, true); } }) .create(); @@ -473,6 +471,7 @@ public class RouteOptionsBottomSheet extends MenuBottomSheetDialogFragment { AvoidRoadsBottomSheetDialogFragment avoidRoadsFragment = new AvoidRoadsBottomSheetDialogFragment(); avoidRoadsFragment.setTargetFragment(RouteOptionsBottomSheet.this, AvoidRoadsBottomSheetDialogFragment.REQUEST_CODE); avoidRoadsFragment.setCompoundButtonColorId(selectedModeColorId); + avoidRoadsFragment.setApplicationMode(applicationMode); avoidRoadsFragment.show(mapActivity.getSupportFragmentManager(), AvoidRoadsBottomSheetDialogFragment.TAG); updateMenu(); } @@ -656,19 +655,6 @@ public class RouteOptionsBottomSheet extends MenuBottomSheetDialogFragment { } - @Override - public void onDismiss(@NonNull DialogInterface dialog) { - super.onDismiss(dialog); - notifyAppModeConfigurationChanged(); - } - - private void notifyAppModeConfigurationChanged() { - Fragment fragment = getTargetFragment(); - if (fragment instanceof OnAppModeConfiguredCallback) { - ((OnAppModeConfiguredCallback) fragment).onAppModeConfigured(); - } - } - private List getRoutingParameters(ApplicationMode applicationMode) { List routingParameters; @@ -742,10 +728,6 @@ public class RouteOptionsBottomSheet extends MenuBottomSheetDialogFragment { } } - public interface OnAppModeConfiguredCallback { - void onAppModeConfigured(); - } - public enum AppModeOptions { CAR(MuteSoundRoutingParameter.KEY, diff --git a/OsmAnd/src/net/osmand/plus/routepreparationmenu/RoutingOptionsHelper.java b/OsmAnd/src/net/osmand/plus/routepreparationmenu/RoutingOptionsHelper.java index d34144cbdc..72e985c353 100644 --- a/OsmAnd/src/net/osmand/plus/routepreparationmenu/RoutingOptionsHelper.java +++ b/OsmAnd/src/net/osmand/plus/routepreparationmenu/RoutingOptionsHelper.java @@ -113,7 +113,7 @@ public class RoutingOptionsHelper { public void selectRestrictedRoads(final MapActivity mapActivity) { mapActivity.getDashboard().setDashboardVisibility(false, DashboardOnMap.DashboardType.ROUTE_PREFERENCES); mapActivity.getMapRouteInfoMenu().hide(); - mapActivity.getMyApplication().getAvoidSpecificRoads().showDialog(mapActivity); + mapActivity.getMyApplication().getAvoidSpecificRoads().showDialog(mapActivity, null); } public void selectVoiceGuidance(final MapActivity mapActivity, final CallbackWithObject callback, ApplicationMode applicationMode) { @@ -230,7 +230,7 @@ public class RoutingOptionsHelper { if (rp instanceof OtherLocalRoutingParameter) { updateGpxRoutingParameter((OtherLocalRoutingParameter) rp); } - routingHelper.recalculateRouteDueToSettingsChange(); + routingHelper.onSettingsChanged(rp.getApplicationMode(), true); } public void updateGpxRoutingParameter(OtherLocalRoutingParameter gpxParam) { @@ -377,7 +377,7 @@ public class RoutingOptionsHelper { LocalRoutingParameter rp = group.getRoutingParameters().get(i); rp.setSelected(settings, i == position); } - mapActivity.getRoutingHelper().recalculateRouteDueToSettingsChange(); + mapActivity.getRoutingHelper().onSettingsChanged(true); if (listener != null) { listener.onClick(); } diff --git a/OsmAnd/src/net/osmand/plus/routepreparationmenu/cards/MapMarkersCard.java b/OsmAnd/src/net/osmand/plus/routepreparationmenu/cards/MapMarkersCard.java index 6367a1df9a..6e18fb0521 100644 --- a/OsmAnd/src/net/osmand/plus/routepreparationmenu/cards/MapMarkersCard.java +++ b/OsmAnd/src/net/osmand/plus/routepreparationmenu/cards/MapMarkersCard.java @@ -108,7 +108,7 @@ public class MapMarkersCard extends BaseCard { public void onClick(View v) { LatLon point = new LatLon(marker.getLatitude(), marker.getLongitude()); app.getTargetPointsHelper().navigateToPoint(point, true, -1, marker.getPointDescription(mapActivity)); - app.getRoutingHelper().recalculateRouteDueToSettingsChange(); + app.getRoutingHelper().onSettingsChanged(true); } }); if (i > 0) { diff --git a/OsmAnd/src/net/osmand/plus/routing/CurrentStreetName.java b/OsmAnd/src/net/osmand/plus/routing/CurrentStreetName.java new file mode 100644 index 0000000000..c984fb9119 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/routing/CurrentStreetName.java @@ -0,0 +1,87 @@ +package net.osmand.plus.routing; + +import androidx.annotation.NonNull; + +import net.osmand.Location; +import net.osmand.binary.RouteDataObject; +import net.osmand.plus.routing.RouteCalculationResult.NextDirectionInfo; +import net.osmand.plus.settings.backend.OsmandSettings; +import net.osmand.router.RouteSegmentResult; +import net.osmand.router.TurnType; +import net.osmand.util.Algorithms; + +public class CurrentStreetName { + public String text; + public TurnType turnType; + public boolean showMarker; // turn type has priority over showMarker + public RouteDataObject shieldObject; + public String exitRef; + + @NonNull + private static String getRouteSegmentStreetName(@NonNull RoutingHelper routingHelper, @NonNull RouteSegmentResult rs, boolean includeRef) { + OsmandSettings settings = routingHelper.getSettings(); + String nm = rs.getObject().getName(settings.MAP_PREFERRED_LOCALE.get(), settings.MAP_TRANSLITERATE_NAMES.get()); + String rf = rs.getObject().getRef(settings.MAP_PREFERRED_LOCALE.get(), settings.MAP_TRANSLITERATE_NAMES.get(), rs.isForwardDirection()); + String dn = rs.getObject().getDestinationName(settings.MAP_PREFERRED_LOCALE.get(), + settings.MAP_TRANSLITERATE_NAMES.get(), rs.isForwardDirection()); + return RoutingHelperUtils.formatStreetName(nm, includeRef ? rf : null, dn, "»"); + } + + @NonNull + public static CurrentStreetName getCurrentName(@NonNull RoutingHelper routingHelper, @NonNull NextDirectionInfo n) { + CurrentStreetName streetName = new CurrentStreetName(); + Location l = routingHelper.getLastFixedLocation(); + float speed = 0; + if (l != null && l.hasSpeed()) { + speed = l.getSpeed(); + } + boolean isSet = false; + // 1. turn is imminent + if (n.distanceTo > 0 && n.directionInfo != null && !n.directionInfo.getTurnType().isSkipToSpeak() && + routingHelper.getVoiceRouter().isDistanceLess(speed, n.distanceTo, routingHelper.getVoiceRouter().PREPARE_DISTANCE * 0.75f)) { + String nm = n.directionInfo.getStreetName(); + String rf = n.directionInfo.getRef(); + String dn = n.directionInfo.getDestinationName(); + isSet = !(Algorithms.isEmpty(nm) && Algorithms.isEmpty(rf) && Algorithms.isEmpty(dn)); + streetName.text = RoutingHelperUtils.formatStreetName(nm, null, dn, "»"); + streetName.turnType = n.directionInfo.getTurnType(); + streetName.shieldObject = n.directionInfo.getRouteDataObject(); + if (streetName.turnType == null) { + streetName.turnType = TurnType.valueOf(TurnType.C, false); + } + if (n.directionInfo.getExitInfo() != null) { + streetName.exitRef = n.directionInfo.getExitInfo().getRef(); + if (!Algorithms.isEmpty(n.directionInfo.getExitInfo().getExitStreetName())) { + streetName.text = n.directionInfo.getExitInfo().getExitStreetName(); + } + } + } + // 2. display current road street name + if (!isSet) { + RouteSegmentResult rs = routingHelper.getCurrentSegmentResult(); + if (rs != null) { + streetName.text = getRouteSegmentStreetName(routingHelper, rs, false); + if (Algorithms.isEmpty(streetName.text)) { + isSet = !Algorithms.isEmpty(getRouteSegmentStreetName(routingHelper, rs, true)); + } else { + isSet = true; + } + streetName.showMarker = true; + streetName.shieldObject = rs.getObject(); + } + } + // 3. display next road street name if this one empty + if (!isSet) { + RouteSegmentResult rs = routingHelper.getNextStreetSegmentResult(); + if (rs != null) { + streetName.text = getRouteSegmentStreetName(routingHelper, rs, false); + streetName.turnType = TurnType.valueOf(TurnType.C, false); + streetName.shieldObject = rs.getObject(); + } + } + if (streetName.turnType == null) { + streetName.showMarker = true; + } + return streetName; + } +} diff --git a/OsmAnd/src/net/osmand/plus/routing/IRouteSettingsListener.java b/OsmAnd/src/net/osmand/plus/routing/IRouteSettingsListener.java new file mode 100644 index 0000000000..0b54434cec --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/routing/IRouteSettingsListener.java @@ -0,0 +1,12 @@ +package net.osmand.plus.routing; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import net.osmand.plus.settings.backend.ApplicationMode; + +public interface IRouteSettingsListener { + + void onRouteSettingsChanged(@Nullable ApplicationMode mode); + +} diff --git a/OsmAnd/src/net/osmand/plus/routing/RouteCalculationParams.java b/OsmAnd/src/net/osmand/plus/routing/RouteCalculationParams.java index fbc361c4d5..7b38b5c91a 100644 --- a/OsmAnd/src/net/osmand/plus/routing/RouteCalculationParams.java +++ b/OsmAnd/src/net/osmand/plus/routing/RouteCalculationParams.java @@ -5,7 +5,6 @@ import net.osmand.data.LatLon; import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.OsmandApplication; import net.osmand.plus.routing.RouteProvider.GPXRouteParams; -import net.osmand.plus.routing.RoutingHelper.RouteCalculationProgressCallback; import net.osmand.router.RouteCalculationProgress; import java.util.List; diff --git a/OsmAnd/src/net/osmand/plus/routing/RouteCalculationProgressCallback.java b/OsmAnd/src/net/osmand/plus/routing/RouteCalculationProgressCallback.java new file mode 100644 index 0000000000..a0649ba109 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/routing/RouteCalculationProgressCallback.java @@ -0,0 +1,12 @@ +package net.osmand.plus.routing; + +public interface RouteCalculationProgressCallback { + + void start(); + + void updateProgress(int progress); + + void requestPrivateAccessRouting(); + + void finish(); +} diff --git a/OsmAnd/src/net/osmand/plus/routing/RouteCalculationResult.java b/OsmAnd/src/net/osmand/plus/routing/RouteCalculationResult.java index a6dcc20fca..e6efcc321f 100644 --- a/OsmAnd/src/net/osmand/plus/routing/RouteCalculationResult.java +++ b/OsmAnd/src/net/osmand/plus/routing/RouteCalculationResult.java @@ -435,7 +435,7 @@ public class RouteCalculationResult { } } - String description = toString(turn, ctx, false) + " " + RoutingHelper.formatStreetName(info.getStreetName(), + String description = toString(turn, ctx, false) + " " + RoutingHelperUtils.formatStreetName(info.getStreetName(), info.getRef(), info.getDestinationName(), ctx.getString(R.string.towards)); description = description.trim(); String[] pointNames = s.getObject().getPointNames(s.getStartPointIndex()); diff --git a/OsmAnd/src/net/osmand/plus/routing/RouteRecalculationHelper.java b/OsmAnd/src/net/osmand/plus/routing/RouteRecalculationHelper.java new file mode 100644 index 0000000000..175003fe18 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/routing/RouteRecalculationHelper.java @@ -0,0 +1,406 @@ +package net.osmand.plus.routing; + +import androidx.annotation.NonNull; + +import net.osmand.Location; +import net.osmand.data.LatLon; +import net.osmand.plus.OsmandApplication; +import net.osmand.plus.R; +import net.osmand.plus.settings.backend.ApplicationMode; +import net.osmand.plus.settings.backend.OsmandSettings; +import net.osmand.router.RouteCalculationProgress; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +import static net.osmand.plus.notifications.OsmandNotification.NotificationType.NAVIGATION; + +class RouteRecalculationHelper { + + private static final int RECALCULATE_THRESHOLD_COUNT_CAUSING_FULL_RECALCULATE = 3; + private static final int RECALCULATE_THRESHOLD_CAUSING_FULL_RECALCULATE_INTERVAL = 2 * 60 * 1000; + + private final OsmandApplication app; + private final RoutingHelper routingHelper; + + private final ExecutorService executor = new RouteRecalculationExecutor(); + private final Map, RouteRecalculationTask> tasksMap = new LinkedHashMap<>(); + private RouteRecalculationTask lastTask; + + private long lastTimeEvaluatedRoute = 0; + private String lastRouteCalcError; + private String lastRouteCalcErrorShort; + private long recalculateCountInInterval = 0; + private int evalWaitInterval = 0; + + private RouteCalculationProgressCallback progressRoute; + + RouteRecalculationHelper(@NonNull RoutingHelper routingHelper) { + this.routingHelper = routingHelper; + this.app = routingHelper.getApplication(); + } + + String getLastRouteCalcError() { + return lastRouteCalcError; + } + + String getLastRouteCalcErrorShort() { + return lastRouteCalcErrorShort; + } + + void setProgressBar(RouteCalculationProgressCallback progressRoute) { + this.progressRoute = progressRoute; + } + + boolean isRouteBeingCalculated() { + synchronized (routingHelper) { + for (Future future : tasksMap.keySet()) { + if (!future.isDone()) { + return true; + } + } + } + return false; + } + + void resetEvalWaitInterval() { + evalWaitInterval = 0; + } + + void stopCalculation() { + synchronized (routingHelper) { + for (Entry, RouteRecalculationTask> taskFuture : tasksMap.entrySet()) { + taskFuture.getValue().stopCalculation(); + taskFuture.getKey().cancel(false); + } + } + } + + void stopCalculationIfParamsNotChanged() { + synchronized (routingHelper) { + boolean hasPendingTasks = tasksMap.isEmpty(); + for (Entry, RouteRecalculationTask> taskFuture : tasksMap.entrySet()) { + RouteRecalculationTask task = taskFuture.getValue(); + if (!task.isParamsChanged()) { + taskFuture.getKey().cancel(false); + task.stopCalculation(); + } + } + if (hasPendingTasks) { + if (isFollowingMode()) { + getVoiceRouter().announceBackOnRoute(); + } + } + } + } + + private OsmandSettings getSettings() { + return routingHelper.getSettings(); + } + + private ApplicationMode getAppMode() { + return routingHelper.getAppMode(); + } + + private boolean isFollowingMode() { + return routingHelper.isFollowingMode(); + } + + private VoiceRouter getVoiceRouter() { + return routingHelper.getVoiceRouter(); + } + + private Location getLastFixedLocation() { + return routingHelper.getLastFixedLocation(); + } + + private boolean isDeviatedFromRoute() { + return routingHelper.isDeviatedFromRoute(); + } + + private Location getLastProjection() { + return routingHelper.getLastProjection(); + } + + private void setNewRoute(RouteCalculationResult prevRoute, final RouteCalculationResult res, Location start) { + final boolean newRoute = !prevRoute.isCalculated(); + if (isFollowingMode()) { + Location lastFixedLocation = getLastFixedLocation(); + if (lastFixedLocation != null) { + start = lastFixedLocation; + } + // try remove false route-recalculated prompts by checking direction to second route node + boolean wrongMovementDirection = false; + List routeNodes = res.getImmutableAllLocations(); + if (routeNodes != null && !routeNodes.isEmpty()) { + int newCurrentRoute = RoutingHelperUtils.lookAheadFindMinOrthogonalDistance(start, routeNodes, res.currentRoute, 15); + if (newCurrentRoute + 1 < routeNodes.size()) { + // This check is valid for Online/GPX services (offline routing is aware of route direction) + wrongMovementDirection = RoutingHelperUtils.checkWrongMovementDirection(start, routeNodes.get(newCurrentRoute + 1)); + // set/reset evalWaitInterval only if new route is in forward direction + if (wrongMovementDirection) { + evalWaitInterval = 3000; + } else { + evalWaitInterval = Math.max(3000, evalWaitInterval * 3 / 2); + evalWaitInterval = Math.min(evalWaitInterval, 120000); + } + + } + } + // trigger voice prompt only if new route is in forward direction + // If route is in wrong direction after one more setLocation it will be recalculated + if (!wrongMovementDirection || newRoute) { + getVoiceRouter().newRouteIsCalculated(newRoute); + } + } + app.getWaypointHelper().setNewRoute(res); + routingHelper.newRouteCalculated(newRoute, res); + } + + void startRouteCalculationThread(RouteCalculationParams params, boolean paramsChanged, boolean updateProgress) { + synchronized (routingHelper) { + getSettings().LAST_ROUTE_APPLICATION_MODE.set(getAppMode()); + RouteRecalculationTask newTask = new RouteRecalculationTask(this, params, paramsChanged); + lastTask = newTask; + startProgress(params); + if (updateProgress) { + updateProgress(params); + } + Future future = executor.submit(newTask); + tasksMap.put(future, newTask); + } + } + + public void recalculateRouteInBackground(final Location start, final LatLon end, final List intermediates, + final RouteProvider.GPXRouteParamsBuilder gpxRoute, final RouteCalculationResult previousRoute, boolean paramsChanged, boolean onlyStartPointChanged) { + if (start == null || end == null) { + return; + } + // do not evaluate very often + if ((!isRouteBeingCalculated() && System.currentTimeMillis() - lastTimeEvaluatedRoute > evalWaitInterval) + || paramsChanged || !onlyStartPointChanged) { + if (System.currentTimeMillis() - lastTimeEvaluatedRoute < RECALCULATE_THRESHOLD_CAUSING_FULL_RECALCULATE_INTERVAL) { + recalculateCountInInterval++; + } + ApplicationMode mode = getAppMode(); + final RouteCalculationParams params = new RouteCalculationParams(); + params.start = start; + params.end = end; + params.intermediates = intermediates; + params.gpxRoute = gpxRoute == null ? null : gpxRoute.build(app); + params.onlyStartPointChanged = onlyStartPointChanged; + if (recalculateCountInInterval < RECALCULATE_THRESHOLD_COUNT_CAUSING_FULL_RECALCULATE + || (gpxRoute != null && gpxRoute.isPassWholeRoute() && isDeviatedFromRoute())) { + params.previousToRecalculate = previousRoute; + } else { + recalculateCountInInterval = 0; + } + params.leftSide = getSettings().DRIVING_REGION.get().leftHandDriving; + params.fast = getSettings().FAST_ROUTE_MODE.getModeValue(mode); + params.mode = mode; + params.ctx = app; + boolean updateProgress = false; + if (params.mode.getRouteService() == RouteProvider.RouteService.OSMAND) { + params.calculationProgress = new RouteCalculationProgress(); + updateProgress = true; + } else { + params.resultListener = new RouteCalculationParams.RouteCalculationResultListener() { + @Override + public void onRouteCalculated(RouteCalculationResult route) { + app.runInUIThread(new Runnable() { + + @Override + public void run() { + finishProgress(params); + } + }); + } + }; + } + if (getLastProjection() != null) { + params.currentLocation = getLastFixedLocation(); + } + startRouteCalculationThread(params, paramsChanged, updateProgress); + } + } + + void startProgress(final RouteCalculationParams params) { + if (params.calculationProgressCallback != null) { + params.calculationProgressCallback.start(); + } else if (progressRoute != null) { + progressRoute.start(); + } + } + + void updateProgress(final RouteCalculationParams params) { + final RouteCalculationProgressCallback progressRoute; + if (params.calculationProgressCallback != null) { + progressRoute = params.calculationProgressCallback; + } else { + progressRoute = this.progressRoute; + } + if (progressRoute != null) { + app.runInUIThread(new Runnable() { + + @Override + public void run() { + RouteCalculationProgress calculationProgress = params.calculationProgress; + if (isRouteBeingCalculated()) { + if (lastTask != null && lastTask.params == params) { + progressRoute.updateProgress((int) calculationProgress.getLinearProgress()); + if (calculationProgress.requestPrivateAccessRouting) { + progressRoute.requestPrivateAccessRouting(); + } + updateProgress(params); + } + } else { + if (calculationProgress.requestPrivateAccessRouting) { + progressRoute.requestPrivateAccessRouting(); + } + progressRoute.finish(); + } + } + }, 300); + } + } + + void finishProgress(RouteCalculationParams params) { + final RouteCalculationProgressCallback progressRoute; + if (params.calculationProgressCallback != null) { + progressRoute = params.calculationProgressCallback; + } else { + progressRoute = this.progressRoute; + } + if (progressRoute != null) { + progressRoute.finish(); + } + } + + private static class RouteRecalculationTask implements Runnable { + + private final RouteRecalculationHelper routingThreadHelper; + private final RoutingHelper routingHelper; + private final RouteCalculationParams params; + private final boolean paramsChanged; + + String routeCalcError; + String routeCalcErrorShort; + int evalWaitInterval = 0; + + public RouteRecalculationTask(@NonNull RouteRecalculationHelper routingThreadHelper, + @NonNull RouteCalculationParams params, boolean paramsChanged) { + this.routingThreadHelper = routingThreadHelper; + this.routingHelper = routingThreadHelper.routingHelper; + this.params = params; + this.paramsChanged = paramsChanged; + if (params.calculationProgress == null) { + params.calculationProgress = new RouteCalculationProgress(); + } + } + + public boolean isParamsChanged() { + return paramsChanged; + } + + public void stopCalculation() { + params.calculationProgress.isCancelled = true; + } + + private OsmandSettings getSettings() { + return routingHelper.getSettings(); + } + + private void showMessage(final String msg) { + final OsmandApplication app = routingHelper.getApplication(); + app.runInUIThread(new Runnable() { + @Override + public void run() { + app.showToastMessage(msg); + } + }); + } + + @Override + public void run() { + RouteProvider provider = routingHelper.getProvider(); + OsmandSettings settings = getSettings(); + RouteCalculationResult res = provider.calculateRouteImpl(params); + if (params.calculationProgress.isCancelled) { + return; + } + final boolean onlineSourceWithoutInternet = !res.isCalculated() && + params.mode.getRouteService().isOnline() && !settings.isInternetConnectionAvailable(); + if (onlineSourceWithoutInternet && settings.GPX_ROUTE_CALC_OSMAND_PARTS.get()) { + if (params.previousToRecalculate != null && params.previousToRecalculate.isCalculated()) { + res = provider.recalculatePartOfflineRoute(res, params); + } + } + RouteCalculationResult prev = routingHelper.getRoute(); + synchronized (routingHelper) { + if (res.isCalculated()) { + if (!params.inSnapToRoadMode && !params.inPublicTransportMode) { + routingHelper.setRoute(res); + routingHelper.updateOriginalRoute(); + } + if (params.resultListener != null) { + params.resultListener.onRouteCalculated(res); + } + } else { + evalWaitInterval = Math.max(3000, routingThreadHelper.evalWaitInterval * 3 / 2); // for Issue #3899 + evalWaitInterval = Math.min(evalWaitInterval, 120000); + } + } + OsmandApplication app = routingHelper.getApplication(); + if (res.isCalculated()) { + if (!params.inSnapToRoadMode && !params.inPublicTransportMode) { + routingThreadHelper.setNewRoute(prev, res, params.start); + } + } else if (onlineSourceWithoutInternet) { + routeCalcError = app.getString(R.string.error_calculating_route) + + ":\n" + app.getString(R.string.internet_connection_required_for_online_route); + routeCalcErrorShort = app.getString(R.string.error_calculating_route); + showMessage(routeCalcError); + } else { + if (res.getErrorMessage() != null) { + routeCalcError = app.getString(R.string.error_calculating_route) + ":\n" + res.getErrorMessage(); + routeCalcErrorShort = app.getString(R.string.error_calculating_route); + } else { + routeCalcError = app.getString(R.string.empty_route_calculated); + routeCalcErrorShort = app.getString(R.string.empty_route_calculated); + } + showMessage(routeCalcError); + } + app.getNotificationHelper().refreshNotification(NAVIGATION); + } + } + + private class RouteRecalculationExecutor extends ThreadPoolExecutor { + + public RouteRecalculationExecutor() { + super(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue()); + } + + protected void afterExecute(Runnable r, Throwable t) { + super.afterExecute(r, t); + RouteRecalculationTask task = null; + synchronized (routingHelper) { + if (r instanceof Future) { + task = tasksMap.remove(r); + } + } + if (t == null && task != null) { + evalWaitInterval = task.evalWaitInterval; + lastRouteCalcError = task.routeCalcError; + lastRouteCalcErrorShort = task.routeCalcErrorShort; + } + lastTimeEvaluatedRoute = System.currentTimeMillis(); + } + } +} diff --git a/OsmAnd/src/net/osmand/plus/routing/RouteSegmentSearchResult.java b/OsmAnd/src/net/osmand/plus/routing/RouteSegmentSearchResult.java new file mode 100644 index 0000000000..dcecfa69be --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/routing/RouteSegmentSearchResult.java @@ -0,0 +1,59 @@ +package net.osmand.plus.routing; + +import net.osmand.binary.RouteDataObject; +import net.osmand.data.QuadPoint; +import net.osmand.router.RouteSegmentResult; +import net.osmand.util.MapUtils; + +import java.util.List; + +public class RouteSegmentSearchResult { + private final int roadIndex; + private final int segmentIndex; + private final QuadPoint point; + + RouteSegmentSearchResult(int roadIndex, int segmentIndex, QuadPoint point) { + this.roadIndex = roadIndex; + this.segmentIndex = segmentIndex; + this.point = point; + } + + public int getRoadIndex() { + return roadIndex; + } + + public int getSegmentIndex() { + return segmentIndex; + } + + public QuadPoint getPoint() { + return point; + } + + public static RouteSegmentSearchResult searchRouteSegment(double latitude, double longitude, double maxDist, List roads) { + int roadIndex = -1; + int segmentIndex = -1; + QuadPoint point = null; + int px = MapUtils.get31TileNumberX(longitude); + int py = MapUtils.get31TileNumberY(latitude); + double dist = maxDist < 0 ? 1000 : maxDist; + for (int i = 0; i < roads.size(); i++) { + RouteSegmentResult road = roads.get(i); + int startPointIndex = Math.min(road.getStartPointIndex(), road.getEndPointIndex()); + int endPointIndex = Math.max(road.getEndPointIndex(), road.getStartPointIndex()); + RouteDataObject obj = road.getObject(); + for (int j = startPointIndex + 1; j <= endPointIndex; j++) { + QuadPoint proj = MapUtils.getProjectionPoint31(px, py, obj.getPoint31XTile(j - 1), obj.getPoint31YTile(j - 1), + obj.getPoint31XTile(j), obj.getPoint31YTile(j)); + double dd = MapUtils.squareRootDist31((int) proj.x, (int) proj.y, px, py); + if (dd < dist) { + dist = dd; + roadIndex = i; + segmentIndex = j; + point = proj; + } + } + } + return roadIndex != -1 ? new RouteSegmentSearchResult(roadIndex, segmentIndex, point) : null; + } +} diff --git a/OsmAnd/src/net/osmand/plus/routing/RoutingHelper.java b/OsmAnd/src/net/osmand/plus/routing/RoutingHelper.java index 49b8babdc8..4b822fa64a 100644 --- a/OsmAnd/src/net/osmand/plus/routing/RoutingHelper.java +++ b/OsmAnd/src/net/osmand/plus/routing/RoutingHelper.java @@ -1,6 +1,5 @@ package net.osmand.plus.routing; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -10,10 +9,7 @@ import net.osmand.LocationsHolder; import net.osmand.PlatformUtil; import net.osmand.ResultMatcher; import net.osmand.ValueHolder; -import net.osmand.binary.RouteDataObject; import net.osmand.data.LatLon; -import net.osmand.data.QuadPoint; -import net.osmand.data.QuadRect; import net.osmand.plus.NavigationService; import net.osmand.plus.OsmAndFormatter; import net.osmand.plus.OsmandApplication; @@ -21,7 +17,6 @@ import net.osmand.plus.OsmandPlugin; import net.osmand.plus.R; import net.osmand.plus.TargetPointsHelper; import net.osmand.plus.TargetPointsHelper.TargetPoint; -import net.osmand.plus.helpers.enums.MetricsConstants; import net.osmand.plus.notifications.OsmandNotification.NotificationType; import net.osmand.plus.routing.RouteCalculationResult.NextDirectionInfo; import net.osmand.plus.routing.RouteProvider.GPXRouteParamsBuilder; @@ -30,12 +25,10 @@ import net.osmand.plus.routing.RouteProvider.RoutingEnvironment; import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.settings.backend.OsmAndAppCustomization.OsmAndAppCustomizationListener; import net.osmand.plus.settings.backend.OsmandSettings; -import net.osmand.router.RouteCalculationProgress; import net.osmand.router.RouteExporter; import net.osmand.router.RoutePlannerFrontEnd.GpxPoint; import net.osmand.router.RoutePlannerFrontEnd.GpxRouteApproximation; import net.osmand.router.RouteSegmentResult; -import net.osmand.router.TurnType; import net.osmand.util.Algorithms; import net.osmand.util.MapUtils; @@ -46,25 +39,27 @@ import java.util.Iterator; import java.util.LinkedList; import java.util.List; -import static net.osmand.plus.notifications.OsmandNotification.NotificationType.NAVIGATION; - public class RoutingHelper { private static final org.apache.commons.logging.Log log = PlatformUtil.getLog(RoutingHelper.class); - private static final float POSITION_TOLERANCE = 60; - private static final int CACHE_RADIUS = 100000; public static final float ALLOWED_DEVIATION = 2; // This should be correlated with RoutingHelper.updateCurrentRouteStatus ( when processed turn now is not announced) - private static int DEFAULT_GPS_TOLERANCE = 12; + private static final int DEFAULT_GPS_TOLERANCE = 12; public static int GPS_TOLERANCE = DEFAULT_GPS_TOLERANCE; public static float ARRIVAL_DISTANCE_FACTOR = 1; private List> listeners = new LinkedList<>(); private List> updateListeners = new LinkedList<>(); - private OsmandApplication app; - private TransportRoutingHelper transportRoutingHelper; + private List> settingsListeners = new LinkedList<>(); + + private final OsmandApplication app; + private OsmandSettings settings; + private final RouteProvider provider; + private final VoiceRouter voiceRouter; + private final RouteRecalculationHelper routeRecalculationHelper; + private final TransportRoutingHelper transportRoutingHelper; private boolean isFollowingMode = false; private boolean isRoutePlanningMode = false; @@ -81,33 +76,15 @@ public class RoutingHelper { private RouteCalculationResult originalRoute = null; - private static final int RECALCULATE_THRESHOLD_COUNT_CAUSING_FULL_RECALCULATE = 3; - private static final int RECALCULATE_THRESHOLD_CAUSING_FULL_RECALCULATE_INTERVAL = 2*60*1000; - private Thread currentRunningJob; - private long lastTimeEvaluatedRoute = 0; - private String lastRouteCalcError; - private String lastRouteCalcErrorShort; - private long recalculateCountInInterval = 0; - private int evalWaitInterval = 0; - private boolean waitingNextJob; private boolean routeWasFinished; private ApplicationMode mode; - private OsmandSettings settings; - - private RouteProvider provider; - private VoiceRouter voiceRouter; private static boolean isDeviatedFromRoute = false; private long deviateFromRouteDetected = 0; //private long wrongMovementDetected = 0; private boolean voiceRouterStopped = false; - private RouteCalculationProgressCallback progressRoute; - -// private ProgressBar progress; -// private Handler progressHandler; - public boolean isDeviatedFromRoute() { return isDeviatedFromRoute; } @@ -116,11 +93,12 @@ public class RoutingHelper { return routeWasFinished; } - public RoutingHelper(OsmandApplication context){ + public RoutingHelper(OsmandApplication context) { this.app = context; settings = context.getSettings(); voiceRouter = new VoiceRouter(this); provider = new RouteProvider(); + routeRecalculationHelper = new RouteRecalculationHelper(this); transportRoutingHelper = context.getTransportRoutingHelper(); transportRoutingHelper.setRoutingHelper(this); setAppMode(settings.APPLICATION_MODE.get()); @@ -134,6 +112,26 @@ public class RoutingHelper { app.getAppCustomization().addListener(customizationListener); } + RouteProvider getProvider() { + return provider; + } + + void resetRouteWasFinished() { + routeWasFinished = false; + } + + void setRoute(RouteCalculationResult route) { + this.route = route; + } + + long getDeviateFromRouteDetected() { + return deviateFromRouteDetected; + } + + void setDeviateFromRouteDetected(long deviateFromRouteDetected) { + this.deviateFromRouteDetected = deviateFromRouteDetected; + } + public TransportRoutingHelper getTransportRoutingHelper() { return transportRoutingHelper; } @@ -147,11 +145,11 @@ public class RoutingHelper { } public String getLastRouteCalcError() { - return lastRouteCalcError; + return routeRecalculationHelper.getLastRouteCalcError(); } public String getLastRouteCalcErrorShort() { - return lastRouteCalcErrorShort; + return routeRecalculationHelper.getLastRouteCalcErrorShort(); } public void setPauseNavigation(boolean b) { @@ -195,8 +193,8 @@ public class RoutingHelper { this.isRoutePlanningMode = isRoutePlanningMode; } - public synchronized void setFinalAndCurrentLocation(LatLon finalLocation, List intermediatePoints, Location currentLocation){ - checkAndUpdateStartLocation(currentLocation); + public synchronized void setFinalAndCurrentLocation(LatLon finalLocation, List intermediatePoints, Location currentLocation) { + RoutingHelperUtils.checkAndUpdateStartLocation(app, currentLocation); RouteCalculationResult previousRoute = route; clearCurrentRoute(finalLocation, intermediatePoints); // to update route @@ -206,17 +204,17 @@ public class RoutingHelper { public synchronized void clearCurrentRoute(LatLon newFinalLocation, List newIntermediatePoints) { route = new RouteCalculationResult(""); isDeviatedFromRoute = false; - evalWaitInterval = 0; + routeRecalculationHelper.resetEvalWaitInterval(); originalRoute = null; app.getWaypointHelper().setNewRoute(route); app.runInUIThread(new Runnable() { @Override public void run() { Iterator> it = listeners.iterator(); - while(it.hasNext()) { + while (it.hasNext()) { WeakReference ref = it.next(); IRouteInformationListener l = ref.get(); - if(l == null) { + if (l == null) { it.remove(); } else { l.routeWasCancelled(); @@ -226,9 +224,7 @@ public class RoutingHelper { }); this.finalLocation = newFinalLocation; this.intermediatePoints = newIntermediatePoints; - if(currentRunningJob instanceof RouteRecalculationThread) { - ((RouteRecalculationThread) currentRunningJob).stopCalculation(); - } + routeRecalculationHelper.stopCalculation(); if (newFinalLocation == null) { settings.FOLLOW_THE_ROUTE.set(false); settings.FOLLOW_THE_GPX_ROUTE.set(null); @@ -245,10 +241,10 @@ public class RoutingHelper { @Override public void run() { Iterator> it = listeners.iterator(); - while(it.hasNext()) { + while (it.hasNext()) { WeakReference ref = it.next(); IRouteInformationListener l = ref.get(); - if(l == null) { + if (l == null) { it.remove(); } else { l.routeWasFinished(); @@ -258,11 +254,38 @@ public class RoutingHelper { }); } + void newRouteCalculated(final boolean newRoute, final RouteCalculationResult res) { + app.runInUIThread(new Runnable() { + @Override + public void run() { + ValueHolder showToast = new ValueHolder<>(); + showToast.value = true; + Iterator> it = listeners.iterator(); + while (it.hasNext()) { + WeakReference ref = it.next(); + IRouteInformationListener l = ref.get(); + if (l == null) { + it.remove(); + } else { + l.newRouteIsCalculated(newRoute, showToast); + } + } + if (showToast.value && newRoute && OsmandPlugin.isDevelopment()) { + String msg = app.getString(R.string.new_route_calculated_dist_dbg, + OsmAndFormatter.getFormattedDistance(res.getWholeDistance(), app), + ((int) res.getRoutingTime()) + " sec", + res.getCalculateTime(), res.getVisitedSegments(), res.getLoadedTiles()); + app.showToastMessage(msg); + } + } + }); + } + public GPXRouteParamsBuilder getCurrentGPXRoute() { return currentGPXRoute; } - public boolean isCurrentGPXRouteV2() { + public boolean isCurrentGPXRouteV2() { return currentGPXRoute != null && RouteExporter.OSMAND_ROUTER_V2.equals(currentGPXRoute.getFile().author); } @@ -274,7 +297,7 @@ public class RoutingHelper { return route.getImmutableAllLocations(); } - public void setAppMode(ApplicationMode mode){ + public void setAppMode(ApplicationMode mode) { this.mode = mode; ARRIVAL_DISTANCE_FACTOR = Math.max(settings.ARRIVAL_DISTANCE_FACTOR.getModeValue(mode), 0.1f); GPS_TOLERANCE = (int) (DEFAULT_GPS_TOLERANCE * ARRIVAL_DISTANCE_FACTOR); @@ -288,27 +311,12 @@ public class RoutingHelper { public LatLon getFinalLocation() { return finalLocation; } - public void checkAndUpdateStartLocation(Location nextStartLocation) { - if (nextStartLocation != null) { - checkAndUpdateStartLocation(new LatLon(nextStartLocation.getLatitude(), nextStartLocation.getLongitude())); - } - } - - public void checkAndUpdateStartLocation(LatLon newStartLocation) { - if (newStartLocation != null) { - LatLon lastStartLocation = app.getSettings().getLastStartPoint(); - if (lastStartLocation == null || MapUtils.getDistance(newStartLocation, lastStartLocation) > CACHE_RADIUS) { - app.getMapViewTrackingUtilities().detectDrivingRegion(newStartLocation); - app.getSettings().setLastStartPoint(newStartLocation); - } - } - } public List getIntermediatePoints() { return intermediatePoints; } - public boolean isRouteCalculated(){ + public boolean isRouteCalculated() { return route.isCalculated(); } @@ -316,7 +324,7 @@ public class RoutingHelper { return voiceRouter; } - public Location getLastProjection(){ + public Location getLastProjection() { return lastProjection; } @@ -324,65 +332,53 @@ public class RoutingHelper { return lastFixedLocation; } - public void addRouteDataListener(IRoutingDataUpdateListener listener) { - updateListeners = updateListenersList(new ArrayList<>(updateListeners), listener, true); + public void addRouteDataListener(@NonNull IRoutingDataUpdateListener listener) { + updateListeners = updateListeners(new ArrayList<>(updateListeners), listener, true); } - public void removeRouteDataListener(IRoutingDataUpdateListener listener) { - updateListeners = updateListenersList(new ArrayList<>(updateListeners), listener, false); + public void removeRouteDataListener(@NonNull IRoutingDataUpdateListener listener) { + updateListeners = updateListeners(new ArrayList<>(updateListeners), listener, false); } - private List> updateListenersList( - List> copyList, - IRoutingDataUpdateListener listener, boolean isNewListener) { - Iterator> it = copyList.iterator(); - while (it.hasNext()) { - WeakReference ref = it.next(); - IRoutingDataUpdateListener l = ref.get(); - if (l == null || l == listener) { - it.remove(); - } - } - if (isNewListener) { - copyList.add(new WeakReference<>(listener)); - } - return copyList; + public void addRouteSettingsListener(@NonNull IRouteSettingsListener listener) { + settingsListeners = updateListeners(new ArrayList<>(settingsListeners), listener, true); } - public void addListener(IRouteInformationListener l){ - listeners = updateInformationListeners(new ArrayList<>(listeners), l, true); + public void removeRouteSettingsListener(@NonNull IRouteSettingsListener listener) { + settingsListeners = updateListeners(new ArrayList<>(settingsListeners), listener, false); + } + + public void addListener(@NonNull IRouteInformationListener l) { + listeners = updateListeners(new ArrayList<>(listeners), l, true); transportRoutingHelper.addListener(l); } - public void removeListener(IRouteInformationListener lt){ - listeners = updateInformationListeners(new ArrayList<>(listeners), lt, false); + public void removeListener(@NonNull IRouteInformationListener lt) { + listeners = updateListeners(new ArrayList<>(listeners), lt, false); } - private List> updateInformationListeners( - List> copyList, - IRouteInformationListener listener, boolean isNewListener) { - Iterator> it = copyList.iterator(); + private List> updateListeners(List> copyList, + T listener, boolean isNewListener) { + Iterator> it = copyList.iterator(); while (it.hasNext()) { - WeakReference ref = it.next(); - IRouteInformationListener l = ref.get(); + WeakReference ref = it.next(); + T l = ref.get(); if (l == null || l == listener) { it.remove(); } } - if (isNewListener) { copyList.add(new WeakReference<>(listener)); } return copyList; } - public void updateLocation(Location currentLocation) { if (settings.getPointToStart() == null && settings.getMyLocationToStart() == null && currentLocation != null) { app.getTargetPointsHelper().setMyLocationPoint( new LatLon(currentLocation.getLatitude(), currentLocation.getLongitude()), false, null); } - if(isFollowingMode() || (settings.getPointToStart() == null && isRoutePlanningMode) || + if (isFollowingMode() || (settings.getPointToStart() == null && isRoutePlanningMode) || app.getLocationProvider().getLocationSimulation().isRouteAnimating()) { setCurrentLocation(currentLocation, false); } @@ -392,43 +388,18 @@ public class RoutingHelper { return setCurrentLocation(currentLocation, returnUpdatedLocation, route, false); } - public double getRouteDeviation(){ + public double getRouteDeviation() { if (route == null || - route.getImmutableAllDirections().size() < 2 || - route.currentRoute == 0){ + route.getImmutableAllDirections().size() < 2 || + route.currentRoute == 0) { return 0; } List routeNodes = route.getImmutableAllLocations(); - return getOrthogonalDistance(lastFixedLocation, routeNodes.get(route.currentRoute -1), routeNodes.get(route.currentRoute)); - } - - - public static float getDefaultAllowedDeviation(OsmandSettings settings, ApplicationMode mode, float posTolerance) { - if (settings.DISABLE_OFFROUTE_RECALC.getModeValue(mode)) { - return -1.0f; - } else if (mode.getRouteService() == RouteService.DIRECT_TO) { - return -1.0f; - } else if (mode.getRouteService() == RouteService.STRAIGHT) { - MetricsConstants mc = settings.METRIC_SYSTEM.getModeValue(mode); - if (mc == MetricsConstants.KILOMETERS_AND_METERS || mc == MetricsConstants.MILES_AND_METERS) { - return 500.f; - } else { - // 1/4 mile - return 482.f; - } - } - return posTolerance * ALLOWED_DEVIATION; - } - - public static float getPosTolerance(float accuracy) { - if(accuracy > 0) { - return POSITION_TOLERANCE / 2 + accuracy; - } - return POSITION_TOLERANCE; + return RoutingHelperUtils.getOrthogonalDistance(lastFixedLocation, routeNodes.get(route.currentRoute - 1), routeNodes.get(route.currentRoute)); } private Location setCurrentLocation(Location currentLocation, boolean returnUpdatedLocation, - RouteCalculationResult previousRoute, boolean targetPointsChanged) { + RouteCalculationResult previousRoute, boolean targetPointsChanged) { Location locationProjection = currentLocation; if (isPublicTransportMode() && currentLocation != null && finalLocation != null && (targetPointsChanged || transportRoutingHelper.getStartLocation() == null)) { @@ -442,14 +413,14 @@ public class RoutingHelper { isDeviatedFromRoute = false; return locationProjection; } - float posTolerance = getPosTolerance(currentLocation.hasAccuracy() ? currentLocation.getAccuracy() : 0); + float posTolerance = RoutingHelperUtils.getPosTolerance(currentLocation.hasAccuracy() ? currentLocation.getAccuracy() : 0); boolean calculateRoute = false; synchronized (this) { isDeviatedFromRoute = false; double distOrth = 0; // 0. Route empty or needs to be extended? Then re-calculate route. - if(route.isEmpty()) { + if (route.isEmpty()) { calculateRoute = true; } else { // 1. Update current route position status according to latest received location @@ -461,13 +432,13 @@ public class RoutingHelper { int currentRoute = route.currentRoute; double allowableDeviation = route.getRouteRecalcDistance(); if (allowableDeviation == 0) { - allowableDeviation = getDefaultAllowedDeviation(settings, route.getAppMode(), posTolerance); + allowableDeviation = RoutingHelperUtils.getDefaultAllowedDeviation(settings, route.getAppMode(), posTolerance); } // 2. Analyze if we need to recalculate route // >100m off current route (sideways) or parameter (for Straight line) if (currentRoute > 0 && allowableDeviation > 0) { - distOrth = getOrthogonalDistance(currentLocation, routeNodes.get(currentRoute - 1), routeNodes.get(currentRoute)); + distOrth = RoutingHelperUtils.getOrthogonalDistance(currentLocation, routeNodes.get(currentRoute - 1), routeNodes.get(currentRoute)); if (distOrth > allowableDeviation) { log.info("Recalculate route, because correlation : " + distOrth); //$NON-NLS-1$ isDeviatedFromRoute = true; @@ -478,7 +449,7 @@ public class RoutingHelper { Location next = route.getNextRouteLocation(); boolean isStraight = route.getRouteService() == RouteService.DIRECT_TO || route.getRouteService() == RouteService.STRAIGHT; - boolean wrongMovementDirection = checkWrongMovementDirection(currentLocation, next); + boolean wrongMovementDirection = RoutingHelperUtils.checkWrongMovementDirection(currentLocation, next); if ((allowableDeviation > 0 && wrongMovementDirection && !isStraight && (currentLocation.distanceTo(routeNodes.get(currentRoute)) > allowableDeviation)) && !settings.DISABLE_WRONG_DIRECTION_RECALC.get()) { log.info("Recalculate route, because wrong movement direction: " + currentLocation.distanceTo(routeNodes.get(currentRoute))); //$NON-NLS-1$ @@ -486,7 +457,7 @@ public class RoutingHelper { calculateRoute = true; } // 4. Identify if UTurn is needed - if (identifyUTurnIsNeeded(currentLocation, posTolerance)) { + if (RoutingHelperUtils.identifyUTurnIsNeeded(this, currentLocation, posTolerance)) { isDeviatedFromRoute = true; } // 5. Update Voice router @@ -509,7 +480,7 @@ public class RoutingHelper { if (currentRoute > 0) { locationProjection = new Location(currentLocation); Location nextLocation = routeNodes.get(currentRoute); - LatLon project = getProject(currentLocation, routeNodes.get(currentRoute - 1), routeNodes.get(currentRoute)); + LatLon project = RoutingHelperUtils.getProject(currentLocation, routeNodes.get(currentRoute - 1), routeNodes.get(currentRoute)); locationProjection.setLatitude(project.getLatitude()); locationProjection.setLongitude(project.getLongitude()); @@ -523,76 +494,39 @@ public class RoutingHelper { } if (calculateRoute) { - recalculateRouteInBackground(currentLocation, finalLocation, intermediatePoints, currentGPXRoute, + routeRecalculationHelper.recalculateRouteInBackground(currentLocation, finalLocation, intermediatePoints, currentGPXRoute, previousRoute.isCalculated() ? previousRoute : null, false, !targetPointsChanged); } else { - Thread job = currentRunningJob; - if(job instanceof RouteRecalculationThread) { - RouteRecalculationThread thread = (RouteRecalculationThread) job; - if(!thread.isParamsChanged()) { - thread.stopCalculation(); - } - if (isFollowingMode){ - voiceRouter.announceBackOnRoute(); - } - } + routeRecalculationHelper.stopCalculationIfParamsNotChanged(); } double projectDist = mode != null && mode.hasFastSpeed() ? posTolerance : posTolerance / 2; - if(returnUpdatedLocation && locationProjection != null && currentLocation.distanceTo(locationProjection) < projectDist) { + if (returnUpdatedLocation && locationProjection != null && currentLocation.distanceTo(locationProjection) < projectDist) { return locationProjection; } else { return currentLocation; } } - private static double getOrthogonalDistance(Location loc, Location from, Location to) { - return MapUtils.getOrthogonalDistance(loc.getLatitude(), - loc.getLongitude(), from.getLatitude(), from.getLongitude(), - to.getLatitude(), to.getLongitude()); - } - - private static LatLon getProject(Location loc, Location from, Location to) { - return MapUtils.getProjection(loc.getLatitude(), - loc.getLongitude(), from.getLatitude(), from.getLongitude(), - to.getLatitude(), to.getLongitude()); - } - - private static int lookAheadFindMinOrthogonalDistance(Location currentLocation, List routeNodes, int currentRoute, int iterations) { - double newDist; - double dist = Double.POSITIVE_INFINITY; - int index = currentRoute; - while (iterations > 0 && currentRoute + 1 < routeNodes.size()) { - newDist = getOrthogonalDistance(currentLocation, routeNodes.get(currentRoute), routeNodes.get(currentRoute + 1)); - if (newDist < dist) { - index = currentRoute; - dist = newDist; - } - currentRoute++; - iterations--; - } - return index; - } - private boolean updateCurrentRouteStatus(Location currentLocation, double posTolerance) { List routeNodes = route.getImmutableAllLocations(); int currentRoute = route.currentRoute; // 1. Try to proceed to next point using orthogonal distance (finding minimum orthogonal dist) while (currentRoute + 1 < routeNodes.size()) { double dist = currentLocation.distanceTo(routeNodes.get(currentRoute)); - if(currentRoute > 0) { - dist = getOrthogonalDistance(currentLocation, routeNodes.get(currentRoute - 1), + if (currentRoute > 0) { + dist = RoutingHelperUtils.getOrthogonalDistance(currentLocation, routeNodes.get(currentRoute - 1), routeNodes.get(currentRoute)); } boolean processed = false; // if we are still too far try to proceed many points // if not then look ahead only 3 in order to catch sharp turns boolean longDistance = dist >= 250; - int newCurrentRoute = lookAheadFindMinOrthogonalDistance(currentLocation, routeNodes, currentRoute, longDistance ? 15 : 8); - double newDist = getOrthogonalDistance(currentLocation, routeNodes.get(newCurrentRoute), + int newCurrentRoute = RoutingHelperUtils.lookAheadFindMinOrthogonalDistance(currentLocation, routeNodes, currentRoute, longDistance ? 15 : 8); + double newDist = RoutingHelperUtils.getOrthogonalDistance(currentLocation, routeNodes.get(newCurrentRoute), routeNodes.get(newCurrentRoute + 1)); - if(longDistance) { - if(newDist < dist) { + if (longDistance) { + if (newDist < dist) { if (log.isDebugEnabled()) { log.debug("Processed by distance : (new) " + newDist + " (old) " + dist); //$NON-NLS-1$//$NON-NLS-2$ } @@ -629,47 +563,39 @@ public class RoutingHelper { route.updateCurrentRoute(newCurrentRoute + 1); currentRoute = newCurrentRoute + 1; app.getNotificationHelper().refreshNotification(NotificationType.NAVIGATION); - if (!updateListeners.isEmpty()) { - ArrayList> tmp = new ArrayList<>(updateListeners); - for (WeakReference ref : tmp) { - IRoutingDataUpdateListener l = ref.get(); - if (l != null) { - l.onRoutingDataUpdate(); - } - } - } + fireRoutingDataUpdateEvent(); } else { break; } } // 2. check if intermediate found - if(route.getIntermediatePointsToPass() > 0 - && route.getDistanceToNextIntermediate(lastFixedLocation) < getArrivalDistance() * 2f && !isRoutePlanningMode) { + if (route.getIntermediatePointsToPass() > 0 + && route.getDistanceToNextIntermediate(lastFixedLocation) < RoutingHelperUtils.getArrivalDistance(mode, settings) * 2f && !isRoutePlanningMode) { showMessage(app.getString(R.string.arrived_at_intermediate_point)); route.passIntermediatePoint(); TargetPointsHelper targets = app.getTargetPointsHelper(); String name = ""; - if(intermediatePoints != null && !intermediatePoints.isEmpty()) { + if (intermediatePoints != null && !intermediatePoints.isEmpty()) { LatLon rm = intermediatePoints.remove(0); List ll = targets.getIntermediatePointsNavigation(); int ind = -1; - for(int i = 0; i < ll.size(); i++) { - if(ll.get(i).point != null && MapUtils.getDistance(ll.get(i).point, rm) < 5) { + for (int i = 0; i < ll.size(); i++) { + if (ll.get(i).point != null && MapUtils.getDistance(ll.get(i).point, rm) < 5) { name = ll.get(i).getOnlyName(); ind = i; break; } } - if(ind >= 0) { + if (ind >= 0) { targets.removeWayPoint(false, ind); } } - if(isFollowingMode) { + if (isFollowingMode) { voiceRouter.arrivedIntermediatePoint(name); } // double check - while(intermediatePoints != null && route.getIntermediatePointsToPass() < intermediatePoints.size()) { + while (intermediatePoints != null && route.getIntermediatePointsToPass() < intermediatePoints.size()) { intermediatePoints.remove(0); } } @@ -677,13 +603,13 @@ public class RoutingHelper { // 3. check if destination found Location lastPoint = routeNodes.get(routeNodes.size() - 1); if (currentRoute > routeNodes.size() - 3 - && currentLocation.distanceTo(lastPoint) < getArrivalDistance() + && currentLocation.distanceTo(lastPoint) < RoutingHelperUtils.getArrivalDistance(mode, settings) && !isRoutePlanningMode) { //showMessage(app.getString(R.string.arrived_at_destination)); TargetPointsHelper targets = app.getTargetPointsHelper(); TargetPoint tp = targets.getPointToNavigate(); String description = tp == null ? "" : tp.getOnlyName(); - if(isFollowingMode) { + if (isFollowingMode) { voiceRouter.arrivedDestinationPoint(description); } boolean onDestinationReached = OsmandPlugin.onDestinationReached(); @@ -695,7 +621,7 @@ public class RoutingHelper { @Override public void run() { settings.LAST_ROUTING_APPLICATION_MODE = settings.APPLICATION_MODE.get(); - //settings.APPLICATION_MODE.set(settings.DEFAULT_APPLICATION_MODE.get()); + //settings.setApplicationMode(settings.DEFAULT_APPLICATION_MODE.get()); } }); finishCurrentRoute(); @@ -717,7 +643,7 @@ public class RoutingHelper { } } - if(nextPoint > 0) { + if (nextPoint > 0) { Location next = routeNodes.get(nextPoint); Location prev = routeNodes.get(nextPoint - 1); float bearing = prev.bearingTo(next); @@ -725,14 +651,14 @@ public class RoutingHelper { double bearingPrev = Math.abs(MapUtils.degreesDiff(bearing, currentLocation.bearingTo(prev))); while (true) { Location mp = MapUtils.calculateMidPoint(prev, next); - if(mp.distanceTo(next) <= 100) { + if (mp.distanceTo(next) <= 100) { break; } double bearingMid = Math.abs(MapUtils.degreesDiff(bearing, currentLocation.bearingTo(mp))); - if(bearingPrev < ANGLE_TO_DECLINE) { + if (bearingPrev < ANGLE_TO_DECLINE) { next = mp; bearingTo = bearingMid; - } else if(bearingTo < ANGLE_TO_DECLINE){ + } else if (bearingTo < ANGLE_TO_DECLINE) { prev = mp; bearingPrev = bearingMid; } else { @@ -746,140 +672,31 @@ public class RoutingHelper { return false; } - private float getArrivalDistance() { - ApplicationMode m = mode == null ? settings.getApplicationMode() : mode; - float defaultSpeed = Math.max(0.3f, m.getDefaultSpeed()); - - /// Used to be: car - 90 m, bicycle - 50 m, pedestrian - 20 m - // return ((float)settings.getApplicationMode().getArrivalDistance()) * settings.ARRIVAL_DISTANCE_FACTOR.getModeValue(m); - // GPS_TOLERANCE - 12 m - // 5 seconds: car - 80 m @ 50 kmh, bicyle - 45 m @ 25 km/h, bicyle - 25 m @ 10 km/h, pedestrian - 18 m @ 4 km/h, - return GPS_TOLERANCE + defaultSpeed * 5 * ARRIVAL_DISTANCE_FACTOR; - } - - - private boolean identifyUTurnIsNeeded(Location currentLocation, double posTolerance) { - if (finalLocation == null || currentLocation == null || !route.isCalculated() || isPublicTransportMode()) { - return false; - } - boolean isOffRoute = false; - if (currentLocation.hasBearing()) { - float bearingMotion = currentLocation.getBearing() ; - Location nextRoutePosition = route.getNextRouteLocation(); - float bearingToRoute = currentLocation.bearingTo(nextRoutePosition); - double diff = MapUtils.degreesDiff(bearingMotion, bearingToRoute); - // 7. Check if you left the route and an unscheduled U-turn would bring you back (also Issue 863) - // This prompt is an interim advice and does only sound if a new route in forward direction could not be found in x seconds - if (Math.abs(diff) > 135f) { - float d = currentLocation.distanceTo(nextRoutePosition); - // 60m tolerance to allow for GPS inaccuracy - if (d > posTolerance) { - // require x sec continuous since first detection - if (deviateFromRouteDetected == 0) { - deviateFromRouteDetected = System.currentTimeMillis(); - } else if ((System.currentTimeMillis() - deviateFromRouteDetected > 10000)) { - isOffRoute = true; - //log.info("bearingMotion is opposite to bearingRoute"); //$NON-NLS-1$ - } - } - } else { - deviateFromRouteDetected = 0; - } - } - return isOffRoute; - } - - /** - * Wrong movement direction is considered when between - * current location bearing (determines by 2 last fixed position or provided) - * and bearing from currentLocation to next (current) point - * the difference is more than 60 degrees - */ - public boolean checkWrongMovementDirection(Location currentLocation, Location nextRouteLocation) { - // measuring without bearing could be really error prone (with last fixed location) - // this code has an effect on route recalculation which should be detected without mistakes - if (currentLocation.hasBearing() && nextRouteLocation != null) { - float bearingMotion = currentLocation.getBearing(); - float bearingToRoute = currentLocation.bearingTo(nextRouteLocation); - double diff = MapUtils.degreesDiff(bearingMotion, bearingToRoute); - if (Math.abs(diff) > 60f) { - // require delay interval since first detection, to avoid false positive - //but leave out for now, as late detection is worse than false positive (it may reset voice router then cause bogus turn and u-turn prompting) - //if (wrongMovementDetected == 0) { - // wrongMovementDetected = System.currentTimeMillis(); - //} else if ((System.currentTimeMillis() - wrongMovementDetected > 500)) { - return true; - //} - } else { - //wrongMovementDetected = 0; - return false; - } - } - //wrongMovementDetected = 0; - return false; - } - - private void setNewRoute(RouteCalculationResult prevRoute, final RouteCalculationResult res, Location start){ - final boolean newRoute = !prevRoute.isCalculated(); - if (isFollowingMode) { - if(lastFixedLocation != null) { - start = lastFixedLocation; - } - // try remove false route-recalculated prompts by checking direction to second route node - boolean wrongMovementDirection = false; - List routeNodes = res.getImmutableAllLocations(); - if (routeNodes != null && !routeNodes.isEmpty()) { - int newCurrentRoute = lookAheadFindMinOrthogonalDistance(start, routeNodes, res.currentRoute, 15); - if (newCurrentRoute + 1 < routeNodes.size()) { - // This check is valid for Online/GPX services (offline routing is aware of route direction) - wrongMovementDirection = checkWrongMovementDirection(start, routeNodes.get(newCurrentRoute + 1)); - // set/reset evalWaitInterval only if new route is in forward direction - if (wrongMovementDirection) { - evalWaitInterval = 3000; - } else { - evalWaitInterval = Math.max(3000, evalWaitInterval * 3 / 2); - evalWaitInterval = Math.min(evalWaitInterval, 120000); - } - + private void fireRoutingDataUpdateEvent() { + if (!updateListeners.isEmpty()) { + ArrayList> tmp = new ArrayList<>(updateListeners); + for (WeakReference ref : tmp) { + IRoutingDataUpdateListener l = ref.get(); + if (l != null) { + l.onRoutingDataUpdate(); } } - - - // trigger voice prompt only if new route is in forward direction - // If route is in wrong direction after one more setLocation it will be recalculated - if (!wrongMovementDirection || newRoute) { - voiceRouter.newRouteIsCalculated(newRoute); - } } - app.getWaypointHelper().setNewRoute(res); - - app.runInUIThread(new Runnable() { - @Override - public void run() { - ValueHolder showToast = new ValueHolder<>(); - showToast.value = true; - Iterator> it = listeners.iterator(); - while (it.hasNext()) { - WeakReference ref = it.next(); - IRouteInformationListener l = ref.get(); - if (l == null) { - it.remove(); - } else { - l.newRouteIsCalculated(newRoute, showToast); - } - } - if (showToast.value && newRoute && OsmandPlugin.isDevelopment()) { - String msg = app.getString(R.string.new_route_calculated_dist_dbg, - OsmAndFormatter.getFormattedDistance(res.getWholeDistance(), app), - ((int)res.getRoutingTime()) + " sec", - res.getCalculateTime(), res.getVisitedSegments(), res.getLoadedTiles()); - app.showToastMessage(msg); - } - } - }); } - public int getLeftDistance(){ + private void fireRouteSettingsChangedEvent(@Nullable ApplicationMode mode) { + if (!settingsListeners.isEmpty()) { + ArrayList> tmp = new ArrayList<>(settingsListeners); + for (WeakReference ref : tmp) { + IRouteSettingsListener l = ref.get(); + if (l != null) { + l.onRouteSettingsChanged(mode); + } + } + } + } + + public int getLeftDistance() { return route.getDistanceToFinish(lastFixedLocation); } @@ -899,7 +716,7 @@ public class RoutingHelper { return settings; } - public String getGeneralRouteInformation(){ + public String getGeneralRouteInformation() { int dist = getLeftDistance(); int hours = getLeftTime() / (60 * 60); int minutes = (getLeftTime() / 60) % 60; @@ -907,14 +724,14 @@ public class RoutingHelper { hours, minutes); } - public Location getLocationFromRouteDirection(RouteDirectionInfo i){ + public Location getLocationFromRouteDirection(RouteDirectionInfo i) { return route.getLocationFromRouteDirection(i); } - public synchronized NextDirectionInfo getNextRouteDirectionInfo(NextDirectionInfo info, boolean toSpeak){ + public synchronized NextDirectionInfo getNextRouteDirectionInfo(NextDirectionInfo info, boolean toSpeak) { NextDirectionInfo i = route.getNextRouteDirectionInfo(info, lastProjection, toSpeak); - if(i != null) { - i.imminent = voiceRouter.calculateImminent(i.distanceTo, lastProjection); + if (i != null) { + i.imminent = voiceRouter.calculateImminent(i.distanceTo, lastProjection); } return i; } @@ -923,257 +740,56 @@ public class RoutingHelper { return route.getCurrentMaxSpeed(); } - - public static String formatStreetName(String name, String ref, String destination, String towards) { - String formattedStreetName = ""; - if (ref != null && ref.length() > 0) { - formattedStreetName = ref; - } - if (name != null && name.length() > 0) { - if (formattedStreetName.length() > 0) { - formattedStreetName = formattedStreetName + " "; - } - formattedStreetName = formattedStreetName + name; - } - if (destination != null && destination.length() > 0) { - if (formattedStreetName.length() > 0) { - formattedStreetName = formattedStreetName + " "; - } - formattedStreetName = formattedStreetName + towards + " " + destination; - } - return formattedStreetName.replace(";", ", "); - - } - - - public static class CurrentStreetName { - public String text; - public TurnType turnType; - public boolean showMarker; // turn type has priority over showMarker - public RouteDataObject shieldObject; - public String exitRef; - } - - public synchronized CurrentStreetName getCurrentName(NextDirectionInfo n){ - CurrentStreetName streetName = new CurrentStreetName(); - Location l = lastFixedLocation; - float speed = 0; - if (l != null && l.hasSpeed()) { - speed = l.getSpeed(); - } - boolean isSet = false; - // 1. turn is imminent - if (n.distanceTo > 0 && n.directionInfo != null && !n.directionInfo.getTurnType().isSkipToSpeak() && - voiceRouter.isDistanceLess(speed, n.distanceTo, voiceRouter.PREPARE_DISTANCE * 0.75f)) { - String nm = n.directionInfo.getStreetName(); - String rf = n.directionInfo.getRef(); - String dn = n.directionInfo.getDestinationName(); - isSet = !(Algorithms.isEmpty(nm) && Algorithms.isEmpty(rf) && Algorithms.isEmpty(dn)); - streetName.text = formatStreetName(nm, null, dn, "»"); - streetName.turnType = n.directionInfo.getTurnType(); - streetName.shieldObject = n.directionInfo.getRouteDataObject(); - if (streetName.turnType == null) { - streetName.turnType = TurnType.valueOf(TurnType.C, false); - } - if (n.directionInfo.getExitInfo() != null) { - streetName.exitRef = n.directionInfo.getExitInfo().getRef(); - if (!Algorithms.isEmpty(n.directionInfo.getExitInfo().getExitStreetName())) { - streetName.text = n.directionInfo.getExitInfo().getExitStreetName(); - } - } - } - // 2. display current road street name - if (!isSet) { - RouteSegmentResult rs = getCurrentSegmentResult(); - if (rs != null) { - streetName.text = getRouteSegmentStreetName(rs, false); - if (Algorithms.isEmpty(streetName.text)) { - isSet = !Algorithms.isEmpty(getRouteSegmentStreetName(rs, true)); - } else { - isSet = true; - } - streetName.showMarker = true; - streetName.shieldObject = rs.getObject(); - } - } - // 3. display next road street name if this one empty - if (!isSet) { - RouteSegmentResult rs = getNextStreetSegmentResult(); - if (rs != null) { - streetName.text = getRouteSegmentStreetName(rs, false); - streetName.turnType = TurnType.valueOf(TurnType.C, false); - streetName.shieldObject = rs.getObject(); - } - } - if (streetName.turnType == null) { - streetName.showMarker = true; - } - return streetName; - } - - private String getRouteSegmentStreetName(RouteSegmentResult rs, boolean includeRef) { - String nm = rs.getObject().getName(settings.MAP_PREFERRED_LOCALE.get(), settings.MAP_TRANSLITERATE_NAMES.get()); - String rf = rs.getObject().getRef(settings.MAP_PREFERRED_LOCALE.get(), settings.MAP_TRANSLITERATE_NAMES.get(), rs.isForwardDirection()); - String dn = rs.getObject().getDestinationName(settings.MAP_PREFERRED_LOCALE.get(), - settings.MAP_TRANSLITERATE_NAMES.get(), rs.isForwardDirection()); - return formatStreetName(nm, includeRef ? rf : null, dn, "»"); + @NonNull + public synchronized CurrentStreetName getCurrentName(NextDirectionInfo n) { + return CurrentStreetName.getCurrentName(this, n); } public RouteSegmentResult getCurrentSegmentResult() { - return route.getCurrentSegmentResult(); - } + return route.getCurrentSegmentResult(); + } public RouteSegmentResult getNextStreetSegmentResult() { return route.getNextStreetSegmentResult(); } - public List getUpcomingTunnel(float distToStart) { - return route.getUpcomingTunnel(distToStart); - } + public List getUpcomingTunnel(float distToStart) { + return route.getUpcomingTunnel(distToStart); + } - public synchronized NextDirectionInfo getNextRouteDirectionInfoAfter(NextDirectionInfo previous, NextDirectionInfo to, boolean toSpeak){ + public synchronized NextDirectionInfo getNextRouteDirectionInfoAfter(NextDirectionInfo previous, NextDirectionInfo to, boolean toSpeak) { NextDirectionInfo i = route.getNextRouteDirectionInfoAfter(previous, to, toSpeak); - if(i != null) { - i.imminent = voiceRouter.calculateImminent(i.distanceTo, null); + if (i != null) { + i.imminent = voiceRouter.calculateImminent(i.distanceTo, null); } return i; } - public List getRouteDirections(){ + public List getRouteDirections() { return route.getRouteDirections(); } - @Nullable - public QuadRect getRouteRect(@NonNull RouteCalculationResult result) { - QuadRect rect = new QuadRect(0, 0, 0, 0); - Location lt = getLastProjection(); - if (lt == null) { - lt = app.getTargetPointsHelper().getPointToStartLocation(); - } - if (lt == null) { - lt = app.getLocationProvider().getLastKnownLocation(); - } - if (lt != null) { - MapUtils.insetLatLonRect(rect, lt.getLatitude(), lt.getLongitude()); - } - List list = result.getImmutableAllLocations(); - for (Location l : list) { - MapUtils.insetLatLonRect(rect, l.getLatitude(), l.getLongitude()); - } - List targetPoints = app.getTargetPointsHelper().getIntermediatePointsWithTarget(); - for (TargetPoint l : targetPoints) { - MapUtils.insetLatLonRect(rect, l.getLatitude(), l.getLongitude()); - } - - return rect.left == 0 && rect.right == 0 ? null : rect; + public void onSettingsChanged() { + onSettingsChanged(false); } - private class RouteRecalculationThread extends Thread { - - private final RouteCalculationParams params; - private boolean paramsChanged; - private Thread prevRunningJob; - - public RouteRecalculationThread(String name, RouteCalculationParams params, boolean paramsChanged) { - super(name); - this.params = params; - this.paramsChanged = paramsChanged; - if(params.calculationProgress == null) { - params.calculationProgress = new RouteCalculationProgress(); - } - } - - public boolean isParamsChanged() { - return paramsChanged; - } - - public void stopCalculation(){ - params.calculationProgress.isCancelled = true; - } - - - @Override - public void run() { - synchronized (RoutingHelper.this) { - routeWasFinished = false; - currentRunningJob = this; - waitingNextJob = prevRunningJob != null; - } - if(prevRunningJob != null) { - while(prevRunningJob.isAlive()){ - try { - Thread.sleep(50); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - synchronized (RoutingHelper.this) { - currentRunningJob = this; - waitingNextJob = false; - } - } - lastRouteCalcError = null; - lastRouteCalcErrorShort = null; - RouteCalculationResult res = provider.calculateRouteImpl(params); - if (params.calculationProgress.isCancelled) { - synchronized (RoutingHelper.this) { - currentRunningJob = null; - } - return; - } - final boolean onlineSourceWithoutInternet = !res.isCalculated() && - params.mode.getRouteService().isOnline() && !settings.isInternetConnectionAvailable(); - if (onlineSourceWithoutInternet && settings.GPX_ROUTE_CALC_OSMAND_PARTS.get()) { - if (params.previousToRecalculate != null && params.previousToRecalculate.isCalculated()) { - res = provider.recalculatePartOfflineRoute(res, params); - } - } - RouteCalculationResult prev = route; - synchronized (RoutingHelper.this) { - if (res.isCalculated()) { - if (!params.inSnapToRoadMode && !params.inPublicTransportMode) { - route = res; - updateOriginalRoute(); - } - if (params.resultListener != null) { - params.resultListener.onRouteCalculated(res); - } - } else { - evalWaitInterval = Math.max(3000, evalWaitInterval * 3 / 2); // for Issue #3899 - evalWaitInterval = Math.min(evalWaitInterval, 120000); - } - currentRunningJob = null; - } - if(res.isCalculated()){ - if (!params.inSnapToRoadMode && !params.inPublicTransportMode) { - setNewRoute(prev, res, params.start); - } - } else if (onlineSourceWithoutInternet) { - lastRouteCalcError = app.getString(R.string.error_calculating_route) - + ":\n" + app.getString(R.string.internet_connection_required_for_online_route); - lastRouteCalcErrorShort = app.getString(R.string.error_calculating_route); - showMessage(lastRouteCalcError); //$NON-NLS-1$ - } else { - if (res.getErrorMessage() != null) { - lastRouteCalcError = app.getString(R.string.error_calculating_route) + ":\n" + res.getErrorMessage(); - lastRouteCalcErrorShort = app.getString(R.string.error_calculating_route); - showMessage(lastRouteCalcError); //$NON-NLS-1$ - } else { - lastRouteCalcError = app.getString(R.string.empty_route_calculated); - lastRouteCalcErrorShort = app.getString(R.string.empty_route_calculated); - showMessage(lastRouteCalcError); - } - } - app.getNotificationHelper().refreshNotification(NAVIGATION); - lastTimeEvaluatedRoute = System.currentTimeMillis(); - } - - public void setWaitPrevJob(Thread prevRunningJob) { - this.prevRunningJob = prevRunningJob; - } + public void onSettingsChanged(boolean forceRouteRecalculation) { + onSettingsChanged(mode, forceRouteRecalculation); } - public void recalculateRouteDueToSettingsChange() { + public void onSettingsChanged(@Nullable ApplicationMode mode) { + onSettingsChanged(mode, false); + } + + public void onSettingsChanged(@Nullable ApplicationMode mode, boolean forceRouteRecalculation) { + if (forceRouteRecalculation || + ((mode == null || mode.equals(this.mode)) && (isRouteCalculated() || isRouteBeingCalculated()))) { + recalculateRouteDueToSettingsChange(); + } + fireRouteSettingsChangedEvent(mode); + } + + private void recalculateRouteDueToSettingsChange() { clearCurrentRoute(finalLocation, intermediatePoints); if (isPublicTransportMode()) { Location start = lastFixedLocation; @@ -1186,140 +802,19 @@ public class RoutingHelper { transportRoutingHelper.recalculateRouteDueToSettingsChange(); } } else { - recalculateRouteInBackground(lastFixedLocation, finalLocation, intermediatePoints, currentGPXRoute, route, true, false); + routeRecalculationHelper.recalculateRouteInBackground(lastFixedLocation, finalLocation, + intermediatePoints, currentGPXRoute, route, true, false); } } - private void recalculateRouteInBackground(final Location start, final LatLon end, final List intermediates, - final GPXRouteParamsBuilder gpxRoute, final RouteCalculationResult previousRoute, boolean paramsChanged, boolean onlyStartPointChanged){ - if (start == null || end == null) { - return; - } - // do not evaluate very often - if ((currentRunningJob == null && System.currentTimeMillis() - lastTimeEvaluatedRoute > evalWaitInterval) - || paramsChanged || !onlyStartPointChanged) { - if(System.currentTimeMillis() - lastTimeEvaluatedRoute < RECALCULATE_THRESHOLD_CAUSING_FULL_RECALCULATE_INTERVAL) { - recalculateCountInInterval ++; - } - final RouteCalculationParams params = new RouteCalculationParams(); - params.start = start; - params.end = end; - params.intermediates = intermediates; - params.gpxRoute = gpxRoute == null ? null : gpxRoute.build(app); - params.onlyStartPointChanged = onlyStartPointChanged; - if (recalculateCountInInterval < RECALCULATE_THRESHOLD_COUNT_CAUSING_FULL_RECALCULATE - || (gpxRoute != null && gpxRoute.isPassWholeRoute() && isDeviatedFromRoute)) { - params.previousToRecalculate = previousRoute; - } else { - recalculateCountInInterval = 0; - } - params.leftSide = settings.DRIVING_REGION.get().leftHandDriving; - params.fast = settings.FAST_ROUTE_MODE.getModeValue(mode); - params.mode = mode; - params.ctx = app; - boolean updateProgress = false; - if (params.mode.getRouteService() == RouteService.OSMAND) { - params.calculationProgress = new RouteCalculationProgress(); - updateProgress = true; - } else { - params.resultListener = new RouteCalculationParams.RouteCalculationResultListener() { - @Override - public void onRouteCalculated(RouteCalculationResult route) { - app.runInUIThread(new Runnable() { - - @Override - public void run() { - finishProgress(params); - } - }); - } - }; - } - if (lastProjection != null) { - params.currentLocation = lastFixedLocation; - } - startRouteCalculationThread(params, paramsChanged, updateProgress); - } - } - - private void updateOriginalRoute() { + void updateOriginalRoute() { if (originalRoute == null) { originalRoute = route; } } - public void startRouteCalculationThread(RouteCalculationParams params, boolean paramsChanged, boolean updateProgress) { - synchronized (this) { - final Thread prevRunningJob = currentRunningJob; - getSettings().LAST_ROUTE_APPLICATION_MODE.set(getAppMode()); - RouteRecalculationThread newThread = new RouteRecalculationThread( - "Calculating route", params, paramsChanged); //$NON-NLS-1$ - currentRunningJob = newThread; - startProgress(params); - if (updateProgress) { - updateProgress(params); - } - if (prevRunningJob != null) { - newThread.setWaitPrevJob(prevRunningJob); - } - currentRunningJob.start(); - } - } - - private void startProgress(final RouteCalculationParams params) { - if (params.calculationProgressCallback != null) { - params.calculationProgressCallback.start(); - } else if (progressRoute != null) { - progressRoute.start(); - } - } - - private void updateProgress(final RouteCalculationParams params) { - final RouteCalculationProgressCallback progressRoute; - if (params.calculationProgressCallback != null) { - progressRoute = params.calculationProgressCallback; - } else { - progressRoute = this.progressRoute; - } - if (progressRoute != null ) { - app.runInUIThread(new Runnable() { - - @Override - public void run() { - RouteCalculationProgress calculationProgress = params.calculationProgress; - if (isRouteBeingCalculated()) { - Thread t = currentRunningJob; - if(t instanceof RouteRecalculationThread && ((RouteRecalculationThread) t).params != params) { - // different calculation started - return; - } else { - progressRoute.updateProgress((int) calculationProgress.getLinearProgress()); - if (calculationProgress.requestPrivateAccessRouting) { - progressRoute.requestPrivateAccessRouting(); - } - updateProgress(params); - } - } else { - if (calculationProgress.requestPrivateAccessRouting) { - progressRoute.requestPrivateAccessRouting(); - } - progressRoute.finish(); - } - } - }, 300); - } - } - - private void finishProgress(RouteCalculationParams params) { - final RouteCalculationProgressCallback progressRoute; - if (params.calculationProgressCallback != null) { - progressRoute = params.calculationProgressCallback; - } else { - progressRoute = this.progressRoute; - } - if (progressRoute != null ) { - progressRoute.finish(); - } + public void startRouteCalculationThread(RouteCalculationParams params) { + routeRecalculationHelper.startRouteCalculationThread(params, true, true); } public static void applyApplicationSettings(RouteCalculationParams params, OsmandSettings settings, ApplicationMode mode) { @@ -1328,18 +823,7 @@ public class RoutingHelper { } public void setProgressBar(RouteCalculationProgressCallback progressRoute) { - this.progressRoute = progressRoute; - } - - public interface RouteCalculationProgressCallback { - - void start(); - - void updateProgress(int progress); - - void requestPrivateAccessRouting(); - - void finish(); + routeRecalculationHelper.setProgressBar(progressRoute); } public boolean isPublicTransportMode() { @@ -1355,10 +839,10 @@ public class RoutingHelper { } public boolean isRouteBeingCalculated() { - return currentRunningJob instanceof RouteRecalculationThread || waitingNextJob; + return routeRecalculationHelper.isRouteBeingCalculated(); } - private void showMessage(final String msg){ + private void showMessage(final String msg) { app.runInUIThread(new Runnable() { @Override public void run() { @@ -1367,8 +851,6 @@ public class RoutingHelper { }); } - - // NEVER returns null @NonNull public RouteCalculationResult getRoute() { return route; @@ -1396,58 +878,7 @@ public class RoutingHelper { public void notifyIfRouteIsCalculated() { if (route.isCalculated()) { - voiceRouter.newRouteIsCalculated(true) ; + voiceRouter.newRouteIsCalculated(true); } } - - public static class RouteSegmentSearchResult { - private int roadIndex; - private int segmentIndex; - private QuadPoint point; - - private RouteSegmentSearchResult(int roadIndex, int segmentIndex, QuadPoint point) { - this.roadIndex = roadIndex; - this.segmentIndex = segmentIndex; - this.point = point; - } - - public int getRoadIndex() { - return roadIndex; - } - - public int getSegmentIndex() { - return segmentIndex; - } - - public QuadPoint getPoint() { - return point; - } - } - - public static RouteSegmentSearchResult searchRouteSegment(double latitude, double longitude, double maxDist, List roads) { - int roadIndex = -1; - int segmentIndex = -1; - QuadPoint point = null; - int px = MapUtils.get31TileNumberX(longitude); - int py = MapUtils.get31TileNumberY(latitude); - double dist = maxDist < 0 ? 1000 : maxDist; - for (int i = 0; i < roads.size(); i++) { - RouteSegmentResult road = roads.get(i); - int startPointIndex = road.getStartPointIndex() < road.getEndPointIndex() ? road.getStartPointIndex() : road.getEndPointIndex(); - int endPointIndex = road.getEndPointIndex() > road.getStartPointIndex() ? road.getEndPointIndex() : road.getStartPointIndex(); - RouteDataObject obj = road.getObject(); - for (int j = startPointIndex + 1; j <= endPointIndex; j++) { - QuadPoint proj = MapUtils.getProjectionPoint31(px, py, obj.getPoint31XTile(j - 1), obj.getPoint31YTile(j - 1), - obj.getPoint31XTile(j), obj.getPoint31YTile(j)); - double dd = MapUtils.squareRootDist31((int) proj.x, (int) proj.y, px, py); - if (dd < dist) { - dist = dd; - roadIndex = i; - segmentIndex = j; - point = proj; - } - } - } - return roadIndex != -1 ? new RouteSegmentSearchResult(roadIndex, segmentIndex, point) : null; - } } diff --git a/OsmAnd/src/net/osmand/plus/routing/RoutingHelperUtils.java b/OsmAnd/src/net/osmand/plus/routing/RoutingHelperUtils.java new file mode 100644 index 0000000000..c696c13585 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/routing/RoutingHelperUtils.java @@ -0,0 +1,210 @@ +package net.osmand.plus.routing; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import net.osmand.Location; +import net.osmand.data.LatLon; +import net.osmand.data.QuadRect; +import net.osmand.plus.OsmandApplication; +import net.osmand.plus.TargetPointsHelper; +import net.osmand.plus.helpers.enums.MetricsConstants; +import net.osmand.plus.settings.backend.ApplicationMode; +import net.osmand.plus.settings.backend.OsmandSettings; +import net.osmand.util.MapUtils; + +import java.util.List; + +public class RoutingHelperUtils { + + private static final float POSITION_TOLERANCE = 60; + private static final int CACHE_RADIUS = 100000; + + @NonNull + public static String formatStreetName(String name, String ref, String destination, String towards) { + String formattedStreetName = ""; + if (ref != null && ref.length() > 0) { + formattedStreetName = ref; + } + if (name != null && name.length() > 0) { + if (formattedStreetName.length() > 0) { + formattedStreetName = formattedStreetName + " "; + } + formattedStreetName = formattedStreetName + name; + } + if (destination != null && destination.length() > 0) { + if (formattedStreetName.length() > 0) { + formattedStreetName = formattedStreetName + " "; + } + formattedStreetName = formattedStreetName + towards + " " + destination; + } + return formattedStreetName.replace(";", ", "); + } + + @Nullable + public static QuadRect getRouteRect(@NonNull OsmandApplication app, @NonNull RouteCalculationResult result) { + QuadRect rect = new QuadRect(0, 0, 0, 0); + Location lt = app.getRoutingHelper().getLastProjection(); + if (lt == null) { + lt = app.getTargetPointsHelper().getPointToStartLocation(); + } + if (lt == null) { + lt = app.getLocationProvider().getLastKnownLocation(); + } + if (lt != null) { + MapUtils.insetLatLonRect(rect, lt.getLatitude(), lt.getLongitude()); + } + List list = result.getImmutableAllLocations(); + for (Location l : list) { + MapUtils.insetLatLonRect(rect, l.getLatitude(), l.getLongitude()); + } + List targetPoints = app.getTargetPointsHelper().getIntermediatePointsWithTarget(); + for (TargetPointsHelper.TargetPoint l : targetPoints) { + MapUtils.insetLatLonRect(rect, l.getLatitude(), l.getLongitude()); + } + + return rect.left == 0 && rect.right == 0 ? null : rect; + } + + static LatLon getProject(Location loc, Location from, Location to) { + return MapUtils.getProjection(loc.getLatitude(), + loc.getLongitude(), from.getLatitude(), from.getLongitude(), + to.getLatitude(), to.getLongitude()); + } + + static double getOrthogonalDistance(Location loc, Location from, Location to) { + return MapUtils.getOrthogonalDistance(loc.getLatitude(), + loc.getLongitude(), from.getLatitude(), from.getLongitude(), + to.getLatitude(), to.getLongitude()); + } + + static int lookAheadFindMinOrthogonalDistance(Location currentLocation, List routeNodes, int currentRoute, int iterations) { + double newDist; + double dist = Double.POSITIVE_INFINITY; + int index = currentRoute; + while (iterations > 0 && currentRoute + 1 < routeNodes.size()) { + newDist = getOrthogonalDistance(currentLocation, routeNodes.get(currentRoute), routeNodes.get(currentRoute + 1)); + if (newDist < dist) { + index = currentRoute; + dist = newDist; + } + currentRoute++; + iterations--; + } + return index; + } + + public static float getPosTolerance(float accuracy) { + if (accuracy > 0) { + return POSITION_TOLERANCE / 2 + accuracy; + } + return POSITION_TOLERANCE; + } + + public static float getDefaultAllowedDeviation(OsmandSettings settings, ApplicationMode mode, float posTolerance) { + if (settings.DISABLE_OFFROUTE_RECALC.getModeValue(mode)) { + return -1.0f; + } else if (mode.getRouteService() == RouteProvider.RouteService.DIRECT_TO) { + return -1.0f; + } else if (mode.getRouteService() == RouteProvider.RouteService.STRAIGHT) { + MetricsConstants mc = settings.METRIC_SYSTEM.getModeValue(mode); + if (mc == MetricsConstants.KILOMETERS_AND_METERS || mc == MetricsConstants.MILES_AND_METERS) { + return 500.f; + } else { + // 1/4 mile + return 482.f; + } + } + return posTolerance * RoutingHelper.ALLOWED_DEVIATION; + } + + /** + * Wrong movement direction is considered when between + * current location bearing (determines by 2 last fixed position or provided) + * and bearing from currentLocation to next (current) point + * the difference is more than 60 degrees + */ + public static boolean checkWrongMovementDirection(Location currentLocation, Location nextRouteLocation) { + // measuring without bearing could be really error prone (with last fixed location) + // this code has an effect on route recalculation which should be detected without mistakes + if (currentLocation.hasBearing() && nextRouteLocation != null) { + float bearingMotion = currentLocation.getBearing(); + float bearingToRoute = currentLocation.bearingTo(nextRouteLocation); + double diff = MapUtils.degreesDiff(bearingMotion, bearingToRoute); + if (Math.abs(diff) > 60f) { + // require delay interval since first detection, to avoid false positive + //but leave out for now, as late detection is worse than false positive (it may reset voice router then cause bogus turn and u-turn prompting) + //if (wrongMovementDetected == 0) { + // wrongMovementDetected = System.currentTimeMillis(); + //} else if ((System.currentTimeMillis() - wrongMovementDetected > 500)) { + return true; + //} + } else { + //wrongMovementDetected = 0; + return false; + } + } + //wrongMovementDetected = 0; + return false; + } + + static boolean identifyUTurnIsNeeded(@NonNull RoutingHelper routingHelper, @NonNull Location currentLocation, double posTolerance) { + RouteCalculationResult route = routingHelper.getRoute(); + if (routingHelper.getFinalLocation() == null || currentLocation == null || !route.isCalculated() || routingHelper.isPublicTransportMode()) { + return false; + } + boolean isOffRoute = false; + if (currentLocation.hasBearing()) { + float bearingMotion = currentLocation.getBearing(); + Location nextRoutePosition = route.getNextRouteLocation(); + float bearingToRoute = currentLocation.bearingTo(nextRoutePosition); + double diff = MapUtils.degreesDiff(bearingMotion, bearingToRoute); + // 7. Check if you left the route and an unscheduled U-turn would bring you back (also Issue 863) + // This prompt is an interim advice and does only sound if a new route in forward direction could not be found in x seconds + if (Math.abs(diff) > 135f) { + float d = currentLocation.distanceTo(nextRoutePosition); + // 60m tolerance to allow for GPS inaccuracy + if (d > posTolerance) { + // require x sec continuous since first detection + long deviateFromRouteDetected = routingHelper.getDeviateFromRouteDetected(); + if (deviateFromRouteDetected == 0) { + routingHelper.setDeviateFromRouteDetected(System.currentTimeMillis()); + } else if ((System.currentTimeMillis() - deviateFromRouteDetected > 10000)) { + isOffRoute = true; + //log.info("bearingMotion is opposite to bearingRoute"); //$NON-NLS-1$ + } + } + } else { + routingHelper.setDeviateFromRouteDetected(0); + } + } + return isOffRoute; + } + + static float getArrivalDistance(ApplicationMode mode, OsmandSettings settings) { + ApplicationMode m = mode == null ? settings.getApplicationMode() : mode; + float defaultSpeed = Math.max(0.3f, m.getDefaultSpeed()); + + /// Used to be: car - 90 m, bicycle - 50 m, pedestrian - 20 m + // return ((float)settings.getApplicationMode().getArrivalDistance()) * settings.ARRIVAL_DISTANCE_FACTOR.getModeValue(m); + // GPS_TOLERANCE - 12 m + // 5 seconds: car - 80 m @ 50 kmh, bicyle - 45 m @ 25 km/h, bicyle - 25 m @ 10 km/h, pedestrian - 18 m @ 4 km/h, + return RoutingHelper.GPS_TOLERANCE + defaultSpeed * 5 * RoutingHelper.ARRIVAL_DISTANCE_FACTOR; + } + + public static void checkAndUpdateStartLocation(@NonNull OsmandApplication app, LatLon newStartLocation) { + if (newStartLocation != null) { + LatLon lastStartLocation = app.getSettings().getLastStartPoint(); + if (lastStartLocation == null || MapUtils.getDistance(newStartLocation, lastStartLocation) > CACHE_RADIUS) { + app.getMapViewTrackingUtilities().detectDrivingRegion(newStartLocation); + app.getSettings().setLastStartPoint(newStartLocation); + } + } + } + + public static void checkAndUpdateStartLocation(@NonNull OsmandApplication app, Location nextStartLocation) { + if (nextStartLocation != null) { + checkAndUpdateStartLocation(app, new LatLon(nextStartLocation.getLatitude(), nextStartLocation.getLongitude())); + } + } +} diff --git a/OsmAnd/src/net/osmand/plus/routing/TransportRoutingHelper.java b/OsmAnd/src/net/osmand/plus/routing/TransportRoutingHelper.java index 4fa70f4c9f..08da774f27 100644 --- a/OsmAnd/src/net/osmand/plus/routing/TransportRoutingHelper.java +++ b/OsmAnd/src/net/osmand/plus/routing/TransportRoutingHelper.java @@ -13,38 +13,42 @@ import net.osmand.binary.BinaryMapIndexReader; import net.osmand.data.LatLon; import net.osmand.data.QuadRect; import net.osmand.osm.edit.Node; -import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandPlugin; -import net.osmand.plus.settings.backend.CommonPreference; -import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.R; import net.osmand.plus.render.NativeOsmandLibrary; import net.osmand.plus.routing.RouteCalculationParams.RouteCalculationResultListener; import net.osmand.plus.routing.RouteProvider.RouteService; -import net.osmand.plus.routing.RoutingHelper.RouteCalculationProgressCallback; +import net.osmand.plus.settings.backend.ApplicationMode; +import net.osmand.plus.settings.backend.CommonPreference; +import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.router.GeneralRouter; - +import net.osmand.router.NativeTransportRoutingResult; import net.osmand.router.RouteCalculationProgress; import net.osmand.router.RoutingConfiguration; import net.osmand.router.TransportRoutePlanner; -import net.osmand.router.TransportRouteResult; import net.osmand.router.TransportRoutePlanner.TransportRouteResultSegment; -import net.osmand.router.TransportRoutingContext; +import net.osmand.router.TransportRouteResult; import net.osmand.router.TransportRoutingConfiguration; -import net.osmand.router.NativeTransportRoutingResult; +import net.osmand.router.TransportRoutingContext; import net.osmand.util.MapUtils; import java.io.IOException; import java.lang.ref.WeakReference; import java.util.HashMap; import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Queue; import java.util.TreeMap; import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; import static net.osmand.plus.notifications.OsmandNotification.NotificationType.NAVIGATION; @@ -54,10 +58,14 @@ public class TransportRoutingHelper { private List> listeners = new LinkedList<>(); - private OsmandApplication app; + private final OsmandApplication app; private ApplicationMode applicationMode = ApplicationMode.PUBLIC_TRANSPORT; private RoutingHelper routingHelper; + private final ExecutorService executor = new RouteRecalculationExecutor(); + private final Map, RouteRecalculationTask> tasksMap = new LinkedHashMap<>(); + private RouteRecalculationTask lastTask; + private List routes; private Map, RouteCalculationResult> walkingRouteSegments; private int currentRoute = -1; @@ -65,11 +73,9 @@ public class TransportRoutingHelper { private LatLon startLocation; private LatLon endLocation; - private Thread currentRunningJob; private String lastRouteCalcError; private String lastRouteCalcErrorShort; private long lastTimeEvaluatedRoute = 0; - private boolean waitingNextJob; private TransportRouteCalculationProgressCallback progressRoute; @@ -171,16 +177,16 @@ public class TransportRoutingHelper { this.currentRoute = currentRoute; } - public void addListener(IRouteInformationListener l){ + public void addListener(IRouteInformationListener l) { listeners.add(new WeakReference<>(l)); } - public boolean removeListener(IRouteInformationListener lt){ + public boolean removeListener(IRouteInformationListener lt) { Iterator> it = listeners.iterator(); - while(it.hasNext()) { + while (it.hasNext()) { WeakReference ref = it.next(); IRouteInformationListener l = ref.get(); - if(l == null || lt == l) { + if (l == null || lt == l) { it.remove(); return true; } @@ -213,18 +219,14 @@ public class TransportRoutingHelper { private void startRouteCalculationThread(TransportRouteCalculationParams params) { synchronized (this) { - final Thread prevRunningJob = currentRunningJob; app.getSettings().LAST_ROUTE_APPLICATION_MODE.set(routingHelper.getAppMode()); - RouteRecalculationThread newThread = - new RouteRecalculationThread("Calculating public transport route", params, - app.getSettings().SAFE_MODE.get() ? null : NativeOsmandLibrary.getLoadedLibrary()); - currentRunningJob = newThread; + RouteRecalculationTask newTask = new RouteRecalculationTask(this, params, + app.getSettings().SAFE_MODE.get() ? null : NativeOsmandLibrary.getLoadedLibrary()); + lastTask = newTask; startProgress(params); updateProgress(params); - if (prevRunningJob != null) { - newThread.setWaitPrevJob(prevRunningJob); - } - currentRunningJob.start(); + Future future = executor.submit(newTask); + tasksMap.put(future, newTask); } } @@ -251,10 +253,7 @@ public class TransportRoutingHelper { if (isRouteBeingCalculated()) { float pr = calculationProgress.getLinearProgress(); progressRoute.updateProgress((int) pr); - Thread t = currentRunningJob; - if (t instanceof RouteRecalculationThread && ((RouteRecalculationThread) t).params != params) { - // different calculation started - } else { + if (lastTask != null && lastTask.params == params) { updateProgress(params); } } else { @@ -269,7 +268,23 @@ public class TransportRoutingHelper { } public boolean isRouteBeingCalculated() { - return currentRunningJob instanceof RouteRecalculationThread || waitingNextJob; + synchronized (this) { + for (Future future : tasksMap.keySet()) { + if (!future.isDone()) { + return true; + } + } + } + return false; + } + + private void stopCalculation() { + synchronized (this) { + for (Map.Entry, RouteRecalculationTask> taskFuture : tasksMap.entrySet()) { + taskFuture.getValue().stopCalculation(); + taskFuture.getKey().cancel(false); + } + } } private void setNewRoute(final List res) { @@ -323,9 +338,7 @@ public class TransportRoutingHelper { } }); this.endLocation = newFinalLocation; - if (currentRunningJob instanceof RouteRecalculationThread) { - ((RouteRecalculationThread) currentRunningJob).stopCalculation(); - } + stopCalculation(); } private void setCurrentLocation(LatLon currentLocation) { @@ -336,15 +349,6 @@ public class TransportRoutingHelper { recalculateRouteInBackground(currentLocation, endLocation); } - private void showMessage(final String msg) { - app.runInUIThread(new Runnable() { - @Override - public void run() { - app.showToastMessage(msg); - } - }); - } - @Nullable public QuadRect getTransportRouteRect(@NonNull TransportRouteResult result) { TransportRoutingHelper transportRoutingHelper = app.getTransportRoutingHelper(); @@ -404,7 +408,7 @@ public class TransportRoutingHelper { } } - private class WalkingRouteSegment { + private static class WalkingRouteSegment { TransportRouteResultSegment s1; TransportRouteResultSegment s2; LatLon start; @@ -437,18 +441,24 @@ public class TransportRoutingHelper { } } - private class RouteRecalculationThread extends Thread { + private static class RouteRecalculationTask implements Runnable { + private final TransportRoutingHelper transportRoutingHelper; + private final RoutingHelper routingHelper; private final TransportRouteCalculationParams params; - private Thread prevRunningJob; private final Queue walkingSegmentsToCalculate = new ConcurrentLinkedQueue<>(); private Map, RouteCalculationResult> walkingRouteSegments = new HashMap<>(); private boolean walkingSegmentsCalculated; - private NativeLibrary lib; + private final NativeLibrary lib; - public RouteRecalculationThread(String name, TransportRouteCalculationParams params, NativeLibrary library) { - super(name); + String routeCalcError; + String routeCalcErrorShort; + + public RouteRecalculationTask(@NonNull TransportRoutingHelper transportRoutingHelper, + @NonNull TransportRouteCalculationParams params, @Nullable NativeLibrary library) { + this.transportRoutingHelper = transportRoutingHelper; + this.routingHelper = transportRoutingHelper.routingHelper; this.params = params; this.lib = library; if (params.calculationProgress == null) { @@ -460,9 +470,9 @@ public class TransportRoutingHelper { params.calculationProgress.isCancelled = true; } - /** * TODO Check if native lib available and calculate route there. + * * @param params * @return * @throws IOException @@ -474,18 +484,18 @@ public class TransportRoutingHelper { BinaryMapIndexReader[] files = params.ctx.getResourceManager().getTransportRoutingMapFiles(); params.params.clear(); OsmandSettings settings = params.ctx.getSettings(); - for(Map.Entry e : config.getRouter(params.mode.getRoutingProfile()).getParameters().entrySet()){ + for (Map.Entry e : config.getRouter(params.mode.getRoutingProfile()).getParameters().entrySet()) { String key = e.getKey(); GeneralRouter.RoutingParameter pr = e.getValue(); String vl; - if(pr.getType() == GeneralRouter.RoutingParameterType.BOOLEAN) { + if (pr.getType() == GeneralRouter.RoutingParameterType.BOOLEAN) { CommonPreference pref = settings.getCustomRoutingBooleanProperty(key, pr.getDefaultBoolean()); Boolean bool = pref.getModeValue(params.mode); vl = bool ? "true" : null; } else { vl = settings.getCustomRoutingProperty(key, "").getModeValue(params.mode); } - if(vl != null && vl.length() > 0) { + if (vl != null && vl.length() > 0) { params.params.put(key, vl); } } @@ -493,7 +503,7 @@ public class TransportRoutingHelper { TransportRoutingConfiguration cfg = new TransportRoutingConfiguration(prouter, params.params); TransportRoutingContext ctx = new TransportRoutingContext(cfg, library, files); - ctx.calculationProgress = params.calculationProgress; + ctx.calculationProgress = params.calculationProgress; if (ctx.library != null && !settings.PT_SAFE_MODE.get()) { NativeTransportRoutingResult[] nativeRes = library.runNativePTRouting( MapUtils.get31TileNumberX(params.start.getLongitude()), @@ -510,7 +520,6 @@ public class TransportRoutingHelper { @Nullable private RouteCalculationParams getWalkingRouteParams() { - ApplicationMode walkingMode = ApplicationMode.PEDESTRIAN; final WalkingRouteSegment walkingRouteSegment = walkingSegmentsToCalculate.poll(); @@ -518,13 +527,14 @@ public class TransportRoutingHelper { return null; } + OsmandApplication app = routingHelper.getApplication(); Location start = new Location(""); start.setLatitude(walkingRouteSegment.start.getLatitude()); start.setLongitude(walkingRouteSegment.start.getLongitude()); LatLon end = new LatLon(walkingRouteSegment.end.getLatitude(), walkingRouteSegment.end.getLongitude()); final float currentDistanceFromBegin = - RouteRecalculationThread.this.params.calculationProgress.distanceFromBegin + + RouteRecalculationTask.this.params.calculationProgress.distanceFromBegin + (walkingRouteSegment.s1 != null ? (float) walkingRouteSegment.s1.getTravelDist() : 0); final RouteCalculationParams params = new RouteCalculationParams(); @@ -548,8 +558,8 @@ public class TransportRoutingHelper { float p = Math.max(params.calculationProgress.distanceFromBegin, params.calculationProgress.distanceFromEnd); - RouteRecalculationThread.this.params.calculationProgress.distanceFromBegin = - Math.max(RouteRecalculationThread.this.params.calculationProgress.distanceFromBegin, currentDistanceFromBegin + p); + RouteRecalculationTask.this.params.calculationProgress.distanceFromBegin = + Math.max(RouteRecalculationTask.this.params.calculationProgress.distanceFromBegin, currentDistanceFromBegin + p); } @Override @@ -564,7 +574,7 @@ public class TransportRoutingHelper { updateProgress(0); RouteCalculationParams walkingRouteParams = getWalkingRouteParams(); if (walkingRouteParams != null) { - routingHelper.startRouteCalculationThread(walkingRouteParams, true, true); + routingHelper.startRouteCalculationThread(walkingRouteParams); } } } @@ -572,7 +582,7 @@ public class TransportRoutingHelper { params.resultListener = new RouteCalculationResultListener() { @Override public void onRouteCalculated(RouteCalculationResult route) { - RouteRecalculationThread.this.walkingRouteSegments.put(new Pair<>(walkingRouteSegment.s1, walkingRouteSegment.s2), route); + RouteRecalculationTask.this.walkingRouteSegments.put(new Pair<>(walkingRouteSegment.s1, walkingRouteSegment.s2), route); } }; @@ -603,7 +613,7 @@ public class TransportRoutingHelper { } RouteCalculationParams walkingRouteParams = getWalkingRouteParams(); if (walkingRouteParams != null) { - routingHelper.startRouteCalculationThread(walkingRouteParams, true, true); + routingHelper.startRouteCalculationThread(walkingRouteParams); // wait until all segments calculated while (!walkingSegmentsCalculated) { try { @@ -620,26 +630,18 @@ public class TransportRoutingHelper { } } + private void showMessage(final String msg) { + final OsmandApplication app = routingHelper.getApplication(); + app.runInUIThread(new Runnable() { + @Override + public void run() { + app.showToastMessage(msg); + } + }); + } + @Override public void run() { - synchronized (TransportRoutingHelper.this) { - currentRunningJob = this; - waitingNextJob = prevRunningJob != null; - } - if (prevRunningJob != null) { - while (prevRunningJob.isAlive()) { - try { - Thread.sleep(50); - } catch (InterruptedException e) { - // ignore - } - } - synchronized (TransportRoutingHelper.this) { - currentRunningJob = this; - waitingNextJob = false; - } - } - List res = null; String error = null; try { @@ -652,38 +654,52 @@ public class TransportRoutingHelper { log.error(e); } if (params.calculationProgress.isCancelled) { - synchronized (TransportRoutingHelper.this) { - currentRunningJob = null; - } return; } - synchronized (TransportRoutingHelper.this) { - routes = res; - TransportRoutingHelper.this.walkingRouteSegments = walkingRouteSegments; + synchronized (transportRoutingHelper) { + transportRoutingHelper.routes = res; + transportRoutingHelper.walkingRouteSegments = walkingRouteSegments; if (res != null) { if (params.resultListener != null) { params.resultListener.onRouteCalculated(res); } } - currentRunningJob = null; } + OsmandApplication app = routingHelper.getApplication(); if (res != null) { - setNewRoute(res); + transportRoutingHelper.setNewRoute(res); } else if (error != null) { - lastRouteCalcError = app.getString(R.string.error_calculating_route) + ":\n" + error; - lastRouteCalcErrorShort = app.getString(R.string.error_calculating_route); - showMessage(lastRouteCalcError); + routeCalcError = app.getString(R.string.error_calculating_route) + ":\n" + error; + routeCalcErrorShort = app.getString(R.string.error_calculating_route); + showMessage(routeCalcError); } else { - lastRouteCalcError = app.getString(R.string.empty_route_calculated); - lastRouteCalcErrorShort = app.getString(R.string.empty_route_calculated); - showMessage(lastRouteCalcError); + routeCalcError = app.getString(R.string.empty_route_calculated); + routeCalcErrorShort = app.getString(R.string.empty_route_calculated); + showMessage(routeCalcError); } app.getNotificationHelper().refreshNotification(NAVIGATION); - lastTimeEvaluatedRoute = System.currentTimeMillis(); + } + } + + private class RouteRecalculationExecutor extends ThreadPoolExecutor { + + public RouteRecalculationExecutor() { + super(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue()); } - public void setWaitPrevJob(Thread prevRunningJob) { - this.prevRunningJob = prevRunningJob; + protected void afterExecute(Runnable r, Throwable t) { + super.afterExecute(r, t); + RouteRecalculationTask task = null; + synchronized (TransportRoutingHelper.this) { + if (r instanceof Future) { + task = tasksMap.remove(r); + } + } + if (t == null && task != null) { + lastRouteCalcError = task.routeCalcError; + lastRouteCalcErrorShort = task.routeCalcErrorShort; + } + lastTimeEvaluatedRoute = System.currentTimeMillis(); } } } diff --git a/OsmAnd/src/net/osmand/plus/routing/VoiceRouter.java b/OsmAnd/src/net/osmand/plus/routing/VoiceRouter.java index 4358938079..86523b6a49 100644 --- a/OsmAnd/src/net/osmand/plus/routing/VoiceRouter.java +++ b/OsmAnd/src/net/osmand/plus/routing/VoiceRouter.java @@ -530,7 +530,7 @@ public class VoiceRouter { } if (currentStatus == STATUS_UNKNOWN) { - // Play "Continue for ..." if (1) after route calculation no other prompt is due, or (2) after a turn if next turn is more than PREPARE_LONG_DISTANCE away + // Play "Continue for ..." if (1) after route calculation if no other prompt is due, or (2) after a turn if next turn is more than PREPARE_LONG_DISTANCE away if ((playGoAheadDist == -1) || (dist > PREPARE_LONG_DISTANCE)) { playGoAheadDist = dist - 3 * TURN_NOW_DISTANCE; } diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/OsmandSettings.java b/OsmAnd/src/net/osmand/plus/settings/backend/OsmandSettings.java index fbac3348e8..b3cd8fb9a6 100644 --- a/OsmAnd/src/net/osmand/plus/settings/backend/OsmandSettings.java +++ b/OsmAnd/src/net/osmand/plus/settings/backend/OsmandSettings.java @@ -238,7 +238,7 @@ public class OsmandSettings { String appModeKey = (String) value; ApplicationMode appMode = ApplicationMode.valueOfStringKey(appModeKey, null); if (appMode != null) { - APPLICATION_MODE.set(appMode); + setApplicationMode(appMode); return true; } } @@ -380,6 +380,18 @@ public class OsmandSettings { public ApplicationMode LAST_ROUTING_APPLICATION_MODE = null; + public boolean setApplicationMode(ApplicationMode appMode) { + return setApplicationMode(appMode, true); + } + + public boolean setApplicationMode(ApplicationMode appMode, boolean markAsLastUsed) { + boolean valueSaved = APPLICATION_MODE.set(appMode); + if (markAsLastUsed && valueSaved) { + LAST_USED_APPLICATION_MODE.set(appMode.getStringKey()); + } + return valueSaved; + } + // this value string is synchronized with settings_pref.xml preference name public final OsmandPreference APPLICATION_MODE = new PreferenceWithListener() { @@ -735,11 +747,20 @@ public class OsmandSettings { public final OsmandPreference LAST_FAV_CATEGORY_ENTERED = new StringPreference(this, "last_fav_category", "").makeGlobal(); + public final OsmandPreference USE_LAST_APPLICATION_MODE_BY_DEFAULT = new BooleanPreference(this, "use_last_application_mode_by_default", false).makeGlobal().makeShared(); + + public final OsmandPreference LAST_USED_APPLICATION_MODE = new StringPreference(this, "last_used_application_mode", ApplicationMode.DEFAULT.getStringKey()).makeGlobal().makeShared(); + public final OsmandPreference DEFAULT_APPLICATION_MODE = new CommonPreference(this, "default_application_mode_string", ApplicationMode.DEFAULT) { @Override protected ApplicationMode getValue(Object prefs, ApplicationMode defaultValue) { - String key = settingsAPI.getString(prefs, getId(), defaultValue.getStringKey()); + String key; + if (USE_LAST_APPLICATION_MODE_BY_DEFAULT.get()) { + key = LAST_USED_APPLICATION_MODE.get(); + } else { + key = settingsAPI.getString(prefs, getId(), defaultValue.getStringKey()); + } return ApplicationMode.valueOfStringKey(key, defaultValue); } @@ -747,7 +768,7 @@ public class OsmandSettings { protected boolean setValue(Object prefs, ApplicationMode val) { boolean valueSaved = settingsAPI.edit(prefs).putString(getId(), val.getStringKey()).commit(); if (valueSaved) { - APPLICATION_MODE.set(val); + setApplicationMode(val); } return valueSaved; diff --git a/OsmAnd/src/net/osmand/plus/settings/bottomsheets/RecalculateRouteInDeviationBottomSheet.java b/OsmAnd/src/net/osmand/plus/settings/bottomsheets/RecalculateRouteInDeviationBottomSheet.java index 99fb5fa73a..40f6889397 100644 --- a/OsmAnd/src/net/osmand/plus/settings/bottomsheets/RecalculateRouteInDeviationBottomSheet.java +++ b/OsmAnd/src/net/osmand/plus/settings/bottomsheets/RecalculateRouteInDeviationBottomSheet.java @@ -15,6 +15,7 @@ import com.google.android.material.slider.Slider; import net.osmand.AndroidUtils; import net.osmand.plus.helpers.enums.MetricsConstants; +import net.osmand.plus.routing.RoutingHelperUtils; import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.OsmAndFormatter; import net.osmand.plus.OsmandApplication; @@ -28,7 +29,6 @@ import net.osmand.plus.base.bottomsheetmenu.simpleitems.LongDescriptionItem; import net.osmand.plus.base.bottomsheetmenu.simpleitems.DividerSpaceItem; import net.osmand.plus.base.bottomsheetmenu.simpleitems.SubtitmeListDividerItem; import net.osmand.plus.base.bottomsheetmenu.simpleitems.TitleItem; -import net.osmand.plus.routing.RoutingHelper; import net.osmand.plus.settings.fragments.ApplyQueryType; import net.osmand.plus.settings.fragments.OnConfirmPreferenceChange; import net.osmand.plus.settings.preferences.SwitchPreferenceEx; @@ -207,8 +207,8 @@ public class RecalculateRouteInDeviationBottomSheet extends BooleanPreferenceBot } private void getDefaultValue() { - currentValue = RoutingHelper.getDefaultAllowedDeviation(settings, appMode, - RoutingHelper.getPosTolerance(0)); + currentValue = RoutingHelperUtils.getDefaultAllowedDeviation(settings, appMode, + RoutingHelperUtils.getPosTolerance(0)); } private int findIndexOfValue(float allowedValue) { diff --git a/OsmAnd/src/net/osmand/plus/settings/bottomsheets/ResetProfilePrefsBottomSheet.java b/OsmAnd/src/net/osmand/plus/settings/bottomsheets/ResetProfilePrefsBottomSheet.java index 225bec203c..e9317533bc 100644 --- a/OsmAnd/src/net/osmand/plus/settings/bottomsheets/ResetProfilePrefsBottomSheet.java +++ b/OsmAnd/src/net/osmand/plus/settings/bottomsheets/ResetProfilePrefsBottomSheet.java @@ -12,6 +12,7 @@ import androidx.core.content.ContextCompat; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; +import net.osmand.plus.profiles.ProfileDataUtils; import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.R; import net.osmand.plus.UiUtilities; @@ -19,7 +20,6 @@ import net.osmand.plus.base.bottomsheetmenu.BaseBottomSheetItem; import net.osmand.plus.base.bottomsheetmenu.BottomSheetItemWithCompoundButton; import net.osmand.plus.base.bottomsheetmenu.BottomSheetItemWithDescription; import net.osmand.plus.base.bottomsheetmenu.simpleitems.TitleItem; -import net.osmand.plus.settings.fragments.BaseSettingsFragment; public class ResetProfilePrefsBottomSheet extends BasePreferenceBottomSheet { @@ -48,7 +48,7 @@ public class ResetProfilePrefsBottomSheet extends BasePreferenceBottomSheet { .setChecked(true) .setCompoundButtonColorId(profileColor) .setButtonTintList(ColorStateList.valueOf(getResolvedColor(profileColor))) - .setDescription(BaseSettingsFragment.getAppModeDescription(ctx, mode)) + .setDescription(ProfileDataUtils.getAppModeDescription(ctx, mode)) .setIcon(getIcon(mode.getIconRes(), profileColor)) .setTitle(mode.toHumanString()) .setBackground(new LayerDrawable(layers)) diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/BaseSettingsFragment.java b/OsmAnd/src/net/osmand/plus/settings/fragments/BaseSettingsFragment.java index daed043002..e2d3244f26 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/BaseSettingsFragment.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/BaseSettingsFragment.java @@ -2,7 +2,6 @@ package net.osmand.plus.settings.fragments; import android.annotation.SuppressLint; import android.app.Activity; -import android.content.Context; import android.content.res.ColorStateList; import android.graphics.Typeface; import android.graphics.drawable.Drawable; @@ -883,17 +882,6 @@ public abstract class BaseSettingsFragment extends PreferenceFragmentCompat impl return listPreference; } - public static String getAppModeDescription(Context ctx, ApplicationMode mode) { - String description; - if (mode.isCustomProfile()) { - description = ctx.getString(R.string.profile_type_user_string); - } else { - description = ctx.getString(R.string.profile_type_osmand_string); - } - - return description; - } - public static boolean showInstance(FragmentActivity activity, SettingsScreenType screenType) { return showInstance(activity, screenType, null); } diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/ConfigureMenuItemsFragment.java b/OsmAnd/src/net/osmand/plus/settings/fragments/ConfigureMenuItemsFragment.java index dee6cdb541..9b55f53128 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/ConfigureMenuItemsFragment.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/ConfigureMenuItemsFragment.java @@ -38,7 +38,6 @@ import net.osmand.plus.dialogs.ConfigureMapMenu; import net.osmand.plus.mapcontextmenu.MapContextMenu; import net.osmand.plus.profiles.SelectCopyAppModeBottomSheet; import net.osmand.plus.settings.backend.ApplicationMode; -import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.settings.bottomsheets.ChangeGeneralProfilesPrefBottomSheet; import net.osmand.plus.settings.fragments.ConfigureMenuRootFragment.ScreenType; import net.osmand.plus.settings.fragments.RearrangeMenuItemsAdapter.MenuItemsAdapterListener; @@ -451,7 +450,7 @@ public class ConfigureMenuItemsFragment extends BaseOsmAndFragment new View.OnClickListener() { @Override public void onClick(View view) { - showResetDialog(); + resetToDefault(); } }))); items.add(new RearrangeMenuAdapterItem(BUTTON, new RearrangeMenuItemsAdapter.ButtonItem( @@ -497,29 +496,18 @@ public class ConfigureMenuItemsFragment extends BaseOsmAndFragment dismissDialog.show(); } - public void showResetDialog() { - Context themedContext = UiUtilities.getThemedContext(getActivity(), nightMode); - AlertDialog.Builder dismissDialog = new AlertDialog.Builder(themedContext); - dismissDialog.setTitle(getString(R.string.shared_string_reset)); - dismissDialog.setMessage(getString(R.string.reset_deafult_order)); - dismissDialog.setNegativeButton(R.string.shared_string_cancel, null); - dismissDialog.setPositiveButton(R.string.shared_string_reset, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - hiddenMenuItems.clear(); - menuItemsOrder.clear(); - wasReset = true; - isChanged = true; - if (screenType == ScreenType.CONTEXT_MENU_ACTIONS) { - mainActionItems.clear(); - } - instantiateContextMenuAdapter(); - initSavedIds(appMode, true); - initMainActionsIds(appMode, true); - rearrangeAdapter.updateItems(getAdapterItems()); - } - }); - dismissDialog.show(); + public void resetToDefault() { + hiddenMenuItems.clear(); + menuItemsOrder.clear(); + wasReset = true; + isChanged = true; + if (screenType == ScreenType.CONTEXT_MENU_ACTIONS) { + mainActionItems.clear(); + } + instantiateContextMenuAdapter(); + initSavedIds(appMode, true); + initMainActionsIds(appMode, true); + rearrangeAdapter.updateItems(getAdapterItems()); } private void dismissFragment() { diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/ConfigureProfileFragment.java b/OsmAnd/src/net/osmand/plus/settings/fragments/ConfigureProfileFragment.java index 48ceba0a11..cea9b0e17c 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/ConfigureProfileFragment.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/ConfigureProfileFragment.java @@ -417,7 +417,7 @@ public class ConfigureProfileFragment extends BaseSettingsFragment implements Co if (!ApplicationMode.values(app).contains(selectedMode)) { ApplicationMode.changeProfileAvailability(selectedMode, true, app); } - settings.APPLICATION_MODE.set(selectedMode); + settings.setApplicationMode(selectedMode); fragmentManager.beginTransaction() .remove(this) .addToBackStack(TAG) diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/GeneralProfileSettingsFragment.java b/OsmAnd/src/net/osmand/plus/settings/fragments/GeneralProfileSettingsFragment.java index 3a81548285..430432981e 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/GeneralProfileSettingsFragment.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/GeneralProfileSettingsFragment.java @@ -344,7 +344,7 @@ public class GeneralProfileSettingsFragment extends BaseSettingsFragment impleme TextView desc = (TextView) v.findViewById(R.id.description); if (item instanceof DrivingRegion) { DrivingRegion drivingRegion = (DrivingRegion) item; - title.setText(getString(drivingRegion.name)); + title.setText(app.getString(drivingRegion.name)); desc.setVisibility(View.VISIBLE); desc.setText(drivingRegion.getDescription(v.getContext())); } else if (item instanceof String) { diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/GlobalSettingsFragment.java b/OsmAnd/src/net/osmand/plus/settings/fragments/GlobalSettingsFragment.java index 06dbd4c852..e957eb7748 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/GlobalSettingsFragment.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/GlobalSettingsFragment.java @@ -2,6 +2,7 @@ package net.osmand.plus.settings.fragments; import android.app.Activity; import android.content.Context; +import android.os.Bundle; import android.util.Pair; import android.widget.ImageView; @@ -16,14 +17,20 @@ import net.osmand.plus.dialogs.ConfigureMapMenu; import net.osmand.plus.dialogs.SendAnalyticsBottomSheetDialogFragment; import net.osmand.plus.dialogs.SendAnalyticsBottomSheetDialogFragment.OnSendAnalyticsPrefsUpdate; import net.osmand.plus.dialogs.SpeedCamerasBottomSheet; +import net.osmand.plus.profiles.SelectProfileBottomSheet; +import net.osmand.plus.profiles.SelectProfileBottomSheet.DialogMode; +import net.osmand.plus.profiles.SelectProfileBottomSheet.OnSelectProfileCallback; import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.settings.preferences.ListPreferenceEx; import net.osmand.plus.settings.preferences.SwitchPreferenceEx; +import static net.osmand.plus.profiles.SelectProfileBottomSheet.PROFILE_KEY_ARG; +import static net.osmand.plus.profiles.SelectProfileBottomSheet.USE_LAST_PROFILE_ARG; + public class GlobalSettingsFragment extends BaseSettingsFragment - implements OnSendAnalyticsPrefsUpdate, OnPreferenceChanged { + implements OnSendAnalyticsPrefsUpdate, OnPreferenceChanged, OnSelectProfileCallback { public static final String TAG = GlobalSettingsFragment.class.getSimpleName(); @@ -94,9 +101,7 @@ public class GlobalSettingsFragment extends BaseSettingsFragment @Override public void onPreferenceChanged(String prefId) { - if (prefId.equals(settings.DEFAULT_APPLICATION_MODE.getId())) { - setupDefaultAppModePref(); - } else if (prefId.equals(settings.PREFERRED_LOCALE.getId())) { + if (prefId.equals(settings.PREFERRED_LOCALE.getId())) { // recreate activity to update locale Activity activity = getActivity(); OsmandApplication app = getMyApplication(); @@ -117,7 +122,13 @@ public class GlobalSettingsFragment extends BaseSettingsFragment @Override public boolean onPreferenceClick(Preference preference) { String prefId = preference.getKey(); - if (settings.SPEED_CAMERAS_UNINSTALLED.getId().equals(prefId) && !settings.SPEED_CAMERAS_UNINSTALLED.get()) { + if (prefId.equals(settings.DEFAULT_APPLICATION_MODE.getId())) { + if (getActivity() != null) { + String defaultModeKey = settings.DEFAULT_APPLICATION_MODE.get().getStringKey(); + SelectProfileBottomSheet.showInstance(getActivity(), + DialogMode.DEFAULT_PROFILE, this, defaultModeKey, false); + } + } else if (settings.SPEED_CAMERAS_UNINSTALLED.getId().equals(prefId) && !settings.SPEED_CAMERAS_UNINSTALLED.get()) { FragmentManager fm = getFragmentManager(); if (fm != null) { SpeedCamerasBottomSheet.showInstance(fm, this); @@ -127,23 +138,20 @@ public class GlobalSettingsFragment extends BaseSettingsFragment } private void setupDefaultAppModePref() { - OsmandApplication app = getMyApplication(); - if (app == null) { - return; + Preference defaultApplicationMode = (Preference) findPreference(settings.DEFAULT_APPLICATION_MODE.getId()); + int iconColor = settings.getApplicationMode().getIconColorInfo().getColor(isNightMode()); + String summary; + int iconId; + if (settings.USE_LAST_APPLICATION_MODE_BY_DEFAULT.get()) { + summary = getString(R.string.shared_string_last_used); + iconId = R.drawable.ic_action_manage_profiles; + } else { + ApplicationMode appMode = settings.DEFAULT_APPLICATION_MODE.get(); + summary = appMode.toHumanString(); + iconId = appMode.getIconRes(); } - ApplicationMode[] appModes = ApplicationMode.values(app).toArray(new ApplicationMode[0]); - String[] entries = new String[appModes.length]; - String[] entryValues = new String[appModes.length]; - for (int i = 0; i < entries.length; i++) { - entries[i] = appModes[i].toHumanString(); - entryValues[i] = appModes[i].getStringKey(); - } - - ListPreferenceEx defaultApplicationMode = (ListPreferenceEx) findPreference(settings.DEFAULT_APPLICATION_MODE.getId()); - defaultApplicationMode.setIcon(getIcon(settings.DEFAULT_APPLICATION_MODE.get().getIconRes(), - settings.getApplicationMode().getIconColorInfo().getColor(isNightMode()))); - defaultApplicationMode.setEntries(entries); - defaultApplicationMode.setEntryValues(entryValues); + defaultApplicationMode.setIcon(getIcon(iconId, iconColor)); + defaultApplicationMode.setSummary(summary); } private void setupPreferredLocalePref() { @@ -219,6 +227,18 @@ public class GlobalSettingsFragment extends BaseSettingsFragment uninstallSpeedCameras.setTitle(uninstalled ? R.string.speed_cameras_removed_descr : R.string.uninstall_speed_cameras); } + @Override + public void onProfileSelected(Bundle args) { + if (args.getBoolean(USE_LAST_PROFILE_ARG)) { + settings.USE_LAST_APPLICATION_MODE_BY_DEFAULT.set(true); + } else { + settings.USE_LAST_APPLICATION_MODE_BY_DEFAULT.set(false); + String value = args.getString(PROFILE_KEY_ARG); + settings.setPreference(settings.DEFAULT_APPLICATION_MODE.getId(), value); + } + setupDefaultAppModePref(); + } + public static Pair getPreferredLocaleIdsAndValues(Context ctx) { // See language list and statistics at: https://hosted.weblate.org/projects/osmand/main/ // Hardy maintenance 2016-05-29: diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/MainSettingsFragment.java b/OsmAnd/src/net/osmand/plus/settings/fragments/MainSettingsFragment.java index 05a2b94d2f..f7ae122851 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/MainSettingsFragment.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/MainSettingsFragment.java @@ -20,8 +20,10 @@ import net.osmand.plus.R; import net.osmand.plus.UiUtilities; import net.osmand.plus.activities.MapActivity; import net.osmand.plus.helpers.AndroidUiHelper; -import net.osmand.plus.profiles.SelectProfileBottomSheetDialogFragment; -import net.osmand.plus.profiles.SelectProfileBottomSheetDialogFragment.SelectProfileListener; +import net.osmand.plus.profiles.ProfileDataUtils; +import net.osmand.plus.profiles.SelectProfileBottomSheet; +import net.osmand.plus.profiles.SelectProfileBottomSheet.DialogMode; +import net.osmand.plus.profiles.SelectProfileBottomSheet.OnSelectProfileCallback; import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.settings.backend.backup.SettingsItem; import net.osmand.plus.settings.backend.backup.SettingsItemType; @@ -33,12 +35,10 @@ import java.util.List; import java.util.Set; import static net.osmand.plus.importfiles.ImportHelper.ImportType.SETTINGS; -import static net.osmand.plus.profiles.SelectProfileBottomSheetDialogFragment.DIALOG_TYPE; -import static net.osmand.plus.profiles.SelectProfileBottomSheetDialogFragment.IS_PROFILE_IMPORTED_ARG; -import static net.osmand.plus.profiles.SelectProfileBottomSheetDialogFragment.PROFILE_KEY_ARG; -import static net.osmand.plus.profiles.SelectProfileBottomSheetDialogFragment.TYPE_BASE_APP_PROFILE; +import static net.osmand.plus.profiles.SelectProfileBottomSheet.IS_PROFILE_IMPORTED_ARG; +import static net.osmand.plus.profiles.SelectProfileBottomSheet.PROFILE_KEY_ARG; -public class MainSettingsFragment extends BaseSettingsFragment { +public class MainSettingsFragment extends BaseSettingsFragment implements OnSelectProfileCallback{ public static final String TAG = MainSettingsFragment.class.getName(); @@ -52,7 +52,6 @@ public class MainSettingsFragment extends BaseSettingsFragment { private List allAppModes; private Set availableAppModes; - private SelectProfileListener selectProfileListener = null; @Override public void onCreate(Bundle savedInstanceState) { @@ -119,15 +118,9 @@ public class MainSettingsFragment extends BaseSettingsFragment { ApplicationMode.valueOfStringKey(prefId, null)); return true; } else if (CREATE_PROFILE.equals(prefId)) { - final SelectProfileBottomSheetDialogFragment dialog = new SelectProfileBottomSheetDialogFragment(); - Bundle bundle = new Bundle(); - bundle.putString(DIALOG_TYPE, TYPE_BASE_APP_PROFILE); - dialog.setArguments(bundle); - dialog.setUsedOnMap(false); - dialog.setAppMode(getSelectedAppMode()); if (getActivity() != null) { - getActivity().getSupportFragmentManager().beginTransaction() - .add(dialog, "select_base_profile").commitAllowingStateLoss(); + SelectProfileBottomSheet.showInstance(getActivity(), + DialogMode.BASE_PROFILE, this, null, false); } } else if (IMPORT_PROFILE.equals(prefId)) { final MapActivity mapActivity = getMapActivity(); @@ -162,7 +155,7 @@ public class MainSettingsFragment extends BaseSettingsFragment { private void setupConfigureProfilePref() { ApplicationMode selectedMode = app.getSettings().APPLICATION_MODE.get(); String title = selectedMode.toHumanString(); - String profileType = getAppModeDescription(getContext(), selectedMode); + String profileType = ProfileDataUtils.getAppModeDescription(getContext(), selectedMode); Preference configureProfile = findPreference(CONFIGURE_PROFILE); configureProfile.setIcon(getAppProfilesIcon(selectedMode, true)); configureProfile.setTitle(title); @@ -200,7 +193,7 @@ public class MainSettingsFragment extends BaseSettingsFragment { pref.setIcon(getAppProfilesIcon(applicationMode, isAppProfileEnabled)); pref.setTitle(applicationMode.toHumanString()); - pref.setSummary(getAppModeDescription(getContext(), applicationMode)); + pref.setSummary(ProfileDataUtils.getAppModeDescription(getContext(), applicationMode)); pref.setChecked(isAppProfileEnabled); pref.setLayoutResource(R.layout.preference_with_descr_dialog_and_switch); pref.setFragment(ConfigureProfileFragment.class.getName()); @@ -222,30 +215,23 @@ public class MainSettingsFragment extends BaseSettingsFragment { : getIcon(iconResId, isNightMode() ? R.color.icon_color_secondary_dark : R.color.icon_color_secondary_light); } - public SelectProfileListener getParentProfileListener() { - if (selectProfileListener == null) { - selectProfileListener = new SelectProfileListener() { - @Override - public void onSelectedType(Bundle args) { - FragmentActivity activity = getActivity(); - if (activity != null) { - FragmentManager fragmentManager = activity.getSupportFragmentManager(); - if (fragmentManager != null) { - String profileKey = args.getString(PROFILE_KEY_ARG); - boolean imported = args.getBoolean(IS_PROFILE_IMPORTED_ARG); - ProfileAppearanceFragment.showInstance(activity, SettingsScreenType.PROFILE_APPEARANCE, - profileKey, imported); - } - } - } - }; - } - return selectProfileListener; - } - @Override public void onPause() { updateRouteInfoMenu(); super.onPause(); } + + @Override + public void onProfileSelected(Bundle args) { + FragmentActivity activity = getActivity(); + if (activity != null) { + FragmentManager fragmentManager = activity.getSupportFragmentManager(); + if (fragmentManager != null) { + String profileKey = args.getString(PROFILE_KEY_ARG); + boolean imported = args.getBoolean(IS_PROFILE_IMPORTED_ARG); + ProfileAppearanceFragment.showInstance(activity, SettingsScreenType.PROFILE_APPEARANCE, + profileKey, imported); + } + } + } } \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/NavigationFragment.java b/OsmAnd/src/net/osmand/plus/settings/fragments/NavigationFragment.java index 47b8270066..0720dc7714 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/NavigationFragment.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/NavigationFragment.java @@ -10,51 +10,38 @@ import androidx.preference.Preference; import androidx.preference.SwitchPreferenceCompat; import net.osmand.plus.measurementtool.MeasurementToolFragment; +import net.osmand.plus.profiles.ProfileDataUtils; import net.osmand.plus.routepreparationmenu.RouteOptionsBottomSheet; import net.osmand.plus.routepreparationmenu.RouteOptionsBottomSheet.DialogMode; import net.osmand.plus.settings.backend.ApplicationMode; -import net.osmand.plus.OsmandApplication; -import net.osmand.plus.OsmandPlugin; import net.osmand.plus.R; import net.osmand.plus.activities.MapActivity; -import net.osmand.plus.profiles.ProfileDataObject; import net.osmand.plus.profiles.RoutingProfileDataObject; import net.osmand.plus.profiles.RoutingProfileDataObject.RoutingProfilesResources; -import net.osmand.plus.profiles.SelectProfileBottomSheetDialogFragment; +import net.osmand.plus.profiles.SelectProfileBottomSheet; +import net.osmand.plus.profiles.SelectProfileBottomSheet.OnSelectProfileCallback; import net.osmand.plus.routing.RouteProvider; import net.osmand.plus.settings.preferences.SwitchPreferenceEx; -import net.osmand.router.GeneralRouter; -import net.osmand.router.RoutingConfiguration; import net.osmand.util.Algorithms; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; import java.util.Map; -import static net.osmand.plus.profiles.SelectProfileBottomSheetDialogFragment.DIALOG_TYPE; -import static net.osmand.plus.profiles.SelectProfileBottomSheetDialogFragment.IS_PROFILE_IMPORTED_ARG; -import static net.osmand.plus.profiles.SelectProfileBottomSheetDialogFragment.PROFILE_KEY_ARG; -import static net.osmand.plus.profiles.SelectProfileBottomSheetDialogFragment.SELECTED_KEY; -import static net.osmand.plus.profiles.SelectProfileBottomSheetDialogFragment.TYPE_NAV_PROFILE; +import static net.osmand.plus.profiles.SelectProfileBottomSheet.IS_PROFILE_IMPORTED_ARG; +import static net.osmand.plus.profiles.SelectProfileBottomSheet.PROFILE_KEY_ARG; import static net.osmand.plus.routepreparationmenu.RouteOptionsBottomSheet.DIALOG_MODE_KEY; -public class NavigationFragment extends BaseSettingsFragment { +public class NavigationFragment extends BaseSettingsFragment implements OnSelectProfileCallback { public static final String TAG = NavigationFragment.class.getSimpleName(); public static final String NAVIGATION_TYPE = "navigation_type"; - public static final String OSMAND_NAVIGATION = "osmand_navigation"; - private SelectProfileBottomSheetDialogFragment.SelectProfileListener navTypeListener; private Map routingProfileDataObjects; private Preference navigationType; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - routingProfileDataObjects = getRoutingProfiles(app); + routingProfileDataObjects = ProfileDataUtils.getRoutingProfiles(app); setupOnBackPressedCallback(); } @@ -134,38 +121,17 @@ public class NavigationFragment extends BaseSettingsFragment { @Override public boolean onPreferenceClick(Preference preference) { if (preference.getKey().equals(NAVIGATION_TYPE)) { - final SelectProfileBottomSheetDialogFragment dialog = new SelectProfileBottomSheetDialogFragment(); - Bundle bundle = new Bundle(); - if (getSelectedAppMode() != null) { - bundle.putString(SELECTED_KEY, getSelectedAppMode().getRoutingProfile()); - } - bundle.putString(DIALOG_TYPE, TYPE_NAV_PROFILE); - dialog.setArguments(bundle); - dialog.setUsedOnMap(false); - dialog.setAppMode(getSelectedAppMode()); + String routingProfileKey = + getSelectedAppMode() != null ? getSelectedAppMode().getRoutingProfile() : null; if (getActivity() != null) { - getActivity().getSupportFragmentManager().beginTransaction() - .add(dialog, "select_nav_type").commitAllowingStateLoss(); + SelectProfileBottomSheet.showInstance( + getActivity(), SelectProfileBottomSheet.DialogMode.NAVIGATION_PROFILE, + this, routingProfileKey, false); } } return false; } - public SelectProfileBottomSheetDialogFragment.SelectProfileListener getNavProfileListener() { - if (navTypeListener == null) { - navTypeListener = new SelectProfileBottomSheetDialogFragment.SelectProfileListener() { - @Override - public void onSelectedType(Bundle args) { - if (args.getBoolean(IS_PROFILE_IMPORTED_ARG)) { - routingProfileDataObjects = getRoutingProfiles(app); - } - updateRoutingProfile(args.getString(PROFILE_KEY_ARG)); - } - }; - } - return navTypeListener; - } - void updateRoutingProfile(String profileKey) { RoutingProfileDataObject selectedRoutingProfileDataObject = routingProfileDataObjects.get(profileKey); if (profileKey == null || selectedRoutingProfileDataObject == null) { @@ -193,112 +159,6 @@ public class NavigationFragment extends BaseSettingsFragment { appMode.setRoutingProfile(profileKey); } - public static List getSortedRoutingProfiles(OsmandApplication app) { - List result = new ArrayList<>(); - Map> routingProfilesByFileNames = getRoutingProfilesByFileNames(app); - List fileNames = new ArrayList<>(routingProfilesByFileNames.keySet()); - Collections.sort(fileNames, new Comparator() { - @Override - public int compare(String s, String t1) { - return s.equals(OSMAND_NAVIGATION) ? -1 : t1.equals(OSMAND_NAVIGATION) ? 1 : s.compareToIgnoreCase(t1); - } - }); - for (String fileName : fileNames) { - List routingProfilesFromFile = routingProfilesByFileNames.get(fileName); - if (routingProfilesFromFile != null) { - Collections.sort(routingProfilesFromFile); - result.addAll(routingProfilesFromFile); - } - } - return result; - } - - public static Map> getRoutingProfilesByFileNames(OsmandApplication app) { - Map> result = new HashMap<>(); - for (final RoutingProfileDataObject profile : getRoutingProfiles(app).values()) { - String fileName = profile.getFileName() != null ? profile.getFileName() : OSMAND_NAVIGATION; - if (result.containsKey(fileName)) { - result.get(fileName).add(profile); - } else { - result.put(fileName, new ArrayList() { - { add(profile); } - }); - } - } - return result; - } - - public static Map getRoutingProfiles(OsmandApplication context) { - Map profilesObjects = new HashMap<>(); - profilesObjects.put(RoutingProfilesResources.STRAIGHT_LINE_MODE.name(), new RoutingProfileDataObject( - RoutingProfilesResources.STRAIGHT_LINE_MODE.name(), - context.getString(RoutingProfilesResources.STRAIGHT_LINE_MODE.getStringRes()), - context.getString(R.string.special_routing_type), - RoutingProfilesResources.STRAIGHT_LINE_MODE.getIconRes(), - false, null)); - profilesObjects.put(RoutingProfilesResources.DIRECT_TO_MODE.name(), new RoutingProfileDataObject( - RoutingProfilesResources.DIRECT_TO_MODE.name(), - context.getString(RoutingProfilesResources.DIRECT_TO_MODE.getStringRes()), - context.getString(R.string.special_routing_type), - RoutingProfilesResources.DIRECT_TO_MODE.getIconRes(), - false, null)); - if (context.getBRouterService() != null) { - profilesObjects.put(RoutingProfilesResources.BROUTER_MODE.name(), new RoutingProfileDataObject( - RoutingProfilesResources.BROUTER_MODE.name(), - context.getString(RoutingProfilesResources.BROUTER_MODE.getStringRes()), - context.getString(R.string.third_party_routing_type), - RoutingProfilesResources.BROUTER_MODE.getIconRes(), - false, null)); - } - - List disabledRouterNames = OsmandPlugin.getDisabledRouterNames(); - for (RoutingConfiguration.Builder builder : context.getAllRoutingConfigs()) { - collectRoutingProfilesFromConfig(context, builder, profilesObjects, disabledRouterNames); - } - return profilesObjects; - } - - private static void collectRoutingProfilesFromConfig(OsmandApplication app, RoutingConfiguration.Builder builder, - Map profilesObjects, List disabledRouterNames) { - for (Map.Entry entry : builder.getAllRouters().entrySet()) { - String routerKey = entry.getKey(); - GeneralRouter router = entry.getValue(); - if (!routerKey.equals("geocoding") && !disabledRouterNames.contains(router.getFilename())) { - int iconRes = R.drawable.ic_action_gdirections_dark; - String name = router.getProfileName(); - String description = app.getString(R.string.osmand_default_routing); - String fileName = router.getFilename(); - if (!Algorithms.isEmpty(fileName)) { - description = fileName; - } else if (RoutingProfilesResources.isRpValue(name.toUpperCase())) { - iconRes = RoutingProfilesResources.valueOf(name.toUpperCase()).getIconRes(); - name = app.getString(RoutingProfilesResources.valueOf(name.toUpperCase()).getStringRes()); - } - profilesObjects.put(routerKey, new RoutingProfileDataObject(routerKey, name, description, - iconRes, false, fileName)); - } - } - } - - public static List getBaseProfiles(OsmandApplication app) { - return getBaseProfiles(app, false); - } - - public static List getBaseProfiles(OsmandApplication app, boolean includeBrowseMap) { - List profiles = new ArrayList<>(); - for (ApplicationMode mode : ApplicationMode.allPossibleValues()) { - if (mode != ApplicationMode.DEFAULT || includeBrowseMap) { - String description = mode.getDescription(); - if (Algorithms.isEmpty(description)) { - description = getAppModeDescription(app, mode); - } - profiles.add(new ProfileDataObject(mode.toHumanString(), description, - mode.getStringKey(), mode.getIconRes(), false, mode.getIconColorInfo())); - } - } - return profiles; - } - private void setupVehicleParametersPref() { Preference vehicleParameters = findPreference("vehicle_parameters"); int iconRes = getSelectedAppMode().getIconRes(); @@ -311,4 +171,12 @@ public class NavigationFragment extends BaseSettingsFragment { mapActivity.getMapRouteInfoMenu().updateMenu(); } } + + @Override + public void onProfileSelected(Bundle args) { + if (args.getBoolean(IS_PROFILE_IMPORTED_ARG)) { + routingProfileDataObjects = ProfileDataUtils.getRoutingProfiles(app); + } + updateRoutingProfile(args.getString(PROFILE_KEY_ARG)); + } } \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/ProfileAppearanceFragment.java b/OsmAnd/src/net/osmand/plus/settings/fragments/ProfileAppearanceFragment.java index 2a15433f26..6913770773 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/ProfileAppearanceFragment.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/ProfileAppearanceFragment.java @@ -51,7 +51,9 @@ import net.osmand.plus.profiles.LocationIcon; import net.osmand.plus.profiles.NavigationIcon; import net.osmand.plus.profiles.ProfileIconColors; import net.osmand.plus.profiles.ProfileIcons; -import net.osmand.plus.profiles.SelectProfileBottomSheetDialogFragment; +import net.osmand.plus.profiles.SelectProfileBottomSheet; +import net.osmand.plus.profiles.SelectProfileBottomSheet.DialogMode; +import net.osmand.plus.profiles.SelectProfileBottomSheet.OnSelectProfileCallback; import net.osmand.plus.routing.RouteProvider; import net.osmand.plus.widgets.FlowLayout; import net.osmand.plus.widgets.OsmandTextFieldBoxes; @@ -64,13 +66,10 @@ import java.util.ArrayList; import java.util.Collections; import static net.osmand.aidlapi.OsmAndCustomizationConstants.DRAWER_SETTINGS_ID; -import static net.osmand.plus.profiles.SelectProfileBottomSheetDialogFragment.DIALOG_TYPE; -import static net.osmand.plus.profiles.SelectProfileBottomSheetDialogFragment.IS_PROFILE_IMPORTED_ARG; -import static net.osmand.plus.profiles.SelectProfileBottomSheetDialogFragment.PROFILE_KEY_ARG; -import static net.osmand.plus.profiles.SelectProfileBottomSheetDialogFragment.SELECTED_KEY; -import static net.osmand.plus.profiles.SelectProfileBottomSheetDialogFragment.TYPE_BASE_APP_PROFILE; +import static net.osmand.plus.profiles.SelectProfileBottomSheet.IS_PROFILE_IMPORTED_ARG; +import static net.osmand.plus.profiles.SelectProfileBottomSheet.PROFILE_KEY_ARG; -public class ProfileAppearanceFragment extends BaseSettingsFragment { +public class ProfileAppearanceFragment extends BaseSettingsFragment implements OnSelectProfileCallback { private static final Log LOG = PlatformUtil.getLog(ProfileAppearanceFragment.class); @@ -98,7 +97,6 @@ public class ProfileAppearanceFragment extends BaseSettingsFragment { private static final String IS_BASE_PROFILE_IMPORTED = "is_base_profile_imported"; private static final String IS_NEW_PROFILE_KEY = "is_new_profile_key"; - private SelectProfileBottomSheetDialogFragment.SelectProfileListener parentProfileListener; private SettingsHelper.SettingsExportListener exportListener; private ProgressDialog progress; @@ -400,18 +398,12 @@ public class ProfileAppearanceFragment extends BaseSettingsFragment { public void onClick(View v) { if (isNewProfile) { hideKeyboard(); - final SelectProfileBottomSheetDialogFragment fragment = new SelectProfileBottomSheetDialogFragment(); - Bundle bundle = new Bundle(); - fragment.setUsedOnMap(false); - fragment.setAppMode(getSelectedAppMode()); - if (changedProfile.parent != null) { - bundle.putString(SELECTED_KEY, changedProfile.parent.getStringKey()); - } - bundle.putString(DIALOG_TYPE, TYPE_BASE_APP_PROFILE); - fragment.setArguments(bundle); + String selectedAppModeKey = + changedProfile.parent != null ? changedProfile.parent.getStringKey() : null; if (getActivity() != null) { - getActivity().getSupportFragmentManager().beginTransaction() - .add(fragment, "select_nav_type").commitAllowingStateLoss(); + SelectProfileBottomSheet.showInstance( + getActivity(), DialogMode.BASE_PROFILE, ProfileAppearanceFragment.this, + selectedAppModeKey, false); } } } @@ -709,20 +701,6 @@ public class ProfileAppearanceFragment extends BaseSettingsFragment { } } - public SelectProfileBottomSheetDialogFragment.SelectProfileListener getParentProfileListener() { - if (parentProfileListener == null) { - parentProfileListener = new SelectProfileBottomSheetDialogFragment.SelectProfileListener() { - @Override - public void onSelectedType(Bundle args) { - String profileKey = args.getString(PROFILE_KEY_ARG); - boolean imported = args.getBoolean(IS_PROFILE_IMPORTED_ARG); - updateParentProfile(profileKey, imported); - } - }; - } - return parentProfileListener; - } - private SettingsHelper.SettingsExportListener getSettingsExportListener() { if (exportListener == null) { exportListener = new SettingsHelper.SettingsExportListener() { @@ -953,6 +931,13 @@ public class ProfileAppearanceFragment extends BaseSettingsFragment { } } + @Override + public void onProfileSelected(Bundle args) { + String profileKey = args.getString(PROFILE_KEY_ARG); + boolean imported = args.getBoolean(IS_PROFILE_IMPORTED_ARG); + updateParentProfile(profileKey, imported); + } + public static boolean showInstance(FragmentActivity activity, SettingsScreenType screenType, @Nullable String appMode, boolean imported) { try { Fragment fragment = Fragment.instantiate(activity, screenType.fragmentName); diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/RouteParametersFragment.java b/OsmAnd/src/net/osmand/plus/settings/fragments/RouteParametersFragment.java index 00f0960dfe..b0e0ad0639 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/RouteParametersFragment.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/RouteParametersFragment.java @@ -33,6 +33,7 @@ import net.osmand.plus.UiUtilities; import net.osmand.plus.Version; import net.osmand.plus.routing.RouteProvider; import net.osmand.plus.routing.RoutingHelper; +import net.osmand.plus.routing.RoutingHelperUtils; import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.settings.backend.BooleanPreference; import net.osmand.plus.settings.backend.CommonPreference; @@ -431,10 +432,7 @@ public class RouteParametersFragment extends BaseSettingsFragment implements OnP public void onClick(DialogInterface dialog, int which) { mode.setStrAngle(angleValue[0]); updateAllSettings(); - RoutingHelper routingHelper = app.getRoutingHelper(); - if (mode.equals(routingHelper.getAppMode()) && (routingHelper.isRouteCalculated() || routingHelper.isRouteBeingCalculated())) { - routingHelper.recalculateRouteDueToSettingsChange(); - } + app.getRoutingHelper().onSettingsChanged(mode); } }); builder.setNegativeButton(R.string.shared_string_cancel, null); @@ -481,7 +479,7 @@ public class RouteParametersFragment extends BaseSettingsFragment implements OnP float allowedValue = settings.ROUTE_RECALCULATION_DISTANCE.getModeValue(appMode); boolean enabled = allowedValue != DISABLE_MODE; if (allowedValue <= 0) { - allowedValue = RoutingHelper.getDefaultAllowedDeviation(settings, appMode, RoutingHelper.getPosTolerance(0)); + allowedValue = RoutingHelperUtils.getDefaultAllowedDeviation(settings, appMode, RoutingHelperUtils.getPosTolerance(0)); } String summary = String.format(getString(R.string.ltr_or_rtl_combine_via_bold_point), enabled ? getString(R.string.shared_string_enabled) : getString(R.string.shared_string_disabled), @@ -642,12 +640,8 @@ public class RouteParametersFragment extends BaseSettingsFragment implements OnP return multiSelectPref; } - private static void recalculateRoute(OsmandApplication app, ApplicationMode mode) { - RoutingHelper routingHelper = app.getRoutingHelper(); - if (mode.equals(routingHelper.getAppMode()) - && (routingHelper.isRouteCalculated() || routingHelper.isRouteBeingCalculated())) { - routingHelper.recalculateRouteDueToSettingsChange(); - } + private static void recalculateRoute(@NonNull OsmandApplication app, ApplicationMode mode) { + app.getRoutingHelper().onSettingsChanged(mode); } private void clearParameters() { diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/VehicleParametersFragment.java b/OsmAnd/src/net/osmand/plus/settings/fragments/VehicleParametersFragment.java index 738c44de35..7885a6700b 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/VehicleParametersFragment.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/VehicleParametersFragment.java @@ -193,11 +193,7 @@ public class VehicleParametersFragment extends BaseSettingsFragment implements O } private void recalculateRoute() { - RoutingHelper routingHelper = app.getRoutingHelper(); - if (getSelectedAppMode().equals(routingHelper.getAppMode()) - && (routingHelper.isRouteCalculated() || routingHelper.isRouteBeingCalculated())) { - routingHelper.recalculateRouteDueToSettingsChange(); - } + app.getRoutingHelper().onSettingsChanged(getSelectedAppMode()); } private void showSeekbarSettingsDialog(@NonNull Activity activity, final boolean defaultSpeedOnly) { @@ -268,10 +264,7 @@ public class VehicleParametersFragment extends BaseSettingsFragment implements O mode.setMinSpeed(minValue[0] / ratio[0]); mode.setMaxSpeed(maxValue[0] / ratio[0]); } - RoutingHelper routingHelper = app.getRoutingHelper(); - if (mode.equals(routingHelper.getAppMode()) && (routingHelper.isRouteCalculated() || routingHelper.isRouteBeingCalculated())) { - routingHelper.recalculateRouteDueToSettingsChange(); - } + app.getRoutingHelper().onSettingsChanged(mode); } }); builder.setNegativeButton(R.string.shared_string_cancel, null); @@ -283,10 +276,7 @@ public class VehicleParametersFragment extends BaseSettingsFragment implements O mode.setMinSpeed(0f); mode.setMaxSpeed(0f); } - RoutingHelper routingHelper = app.getRoutingHelper(); - if (mode.equals(routingHelper.getAppMode()) && (routingHelper.isRouteCalculated() || routingHelper.isRouteBeingCalculated())) { - routingHelper.recalculateRouteDueToSettingsChange(); - } + app.getRoutingHelper().onSettingsChanged(mode); } }); diff --git a/OsmAnd/src/net/osmand/plus/views/layers/MapControlsLayer.java b/OsmAnd/src/net/osmand/plus/views/layers/MapControlsLayer.java index e7cc9b4466..b2697cecf2 100644 --- a/OsmAnd/src/net/osmand/plus/views/layers/MapControlsLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/layers/MapControlsLayer.java @@ -497,7 +497,7 @@ public class MapControlsLayer extends OsmandMapLayer { if (object instanceof SelectedGpxPoint && !((SelectedGpxPoint) object).getSelectedGpxFile().isShowCurrentTrack()) { GPXFile gpxFile = ((SelectedGpxPoint) object).getSelectedGpxFile().getGpxFile(); mapActivity.getMapActions().enterRoutePlanningModeGivenGpx(gpxFile, null, null, true, true, MenuState.HEADER_ONLY); - routingHelper.recalculateRouteDueToSettingsChange(); + routingHelper.onSettingsChanged(true); menu.close(); } else { if (routingHelper.isFollowingMode() || routingHelper.isRoutePlanningMode()) { @@ -800,7 +800,7 @@ public class MapControlsLayer extends OsmandMapLayer { if (routingHelper.isFollowingMode()) { switchToRouteFollowingLayout(); if (app.getSettings().APPLICATION_MODE.get() != routingHelper.getAppMode()) { - app.getSettings().APPLICATION_MODE.set(routingHelper.getAppMode()); + app.getSettings().setApplicationMode(routingHelper.getAppMode(), false); } } else { if (!app.getTargetPointsHelper().checkPointToNavigateShort()) { @@ -808,7 +808,7 @@ public class MapControlsLayer extends OsmandMapLayer { } else { touchEvent = 0; app.logEvent("start_navigation"); - app.getSettings().APPLICATION_MODE.set(routingHelper.getAppMode()); + app.getSettings().setApplicationMode(routingHelper.getAppMode(), false); mapActivity.getMapViewTrackingUtilities().backToLocationImpl(17, true); app.getSettings().FOLLOW_THE_ROUTE.set(true); routingHelper.setFollowingMode(true); diff --git a/OsmAnd/src/net/osmand/plus/views/mapwidgets/MapInfoWidgetsFactory.java b/OsmAnd/src/net/osmand/plus/views/mapwidgets/MapInfoWidgetsFactory.java index d8101b487d..fca0ae429e 100644 --- a/OsmAnd/src/net/osmand/plus/views/mapwidgets/MapInfoWidgetsFactory.java +++ b/OsmAnd/src/net/osmand/plus/views/mapwidgets/MapInfoWidgetsFactory.java @@ -49,6 +49,8 @@ import net.osmand.plus.OsmAndFormatter; import net.osmand.plus.OsmAndLocationProvider; import net.osmand.plus.OsmAndLocationProvider.GPSInfo; import net.osmand.plus.OsmandApplication; +import net.osmand.plus.routing.CurrentStreetName; +import net.osmand.plus.routing.RoutingHelperUtils; import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.R; import net.osmand.plus.UiUtilities; @@ -933,7 +935,7 @@ public class MapInfoWidgetsFactory { public boolean updateInfo(DrawSettings d) { - RoutingHelper.CurrentStreetName streetName = null; + CurrentStreetName streetName = null; boolean showClosestWaypointFirstInAddress = true; if (routingHelper != null && routingHelper.isRouteCalculated() && !routingHelper.isDeviatedFromRoute()) { if (routingHelper.isFollowingMode()) { @@ -953,11 +955,11 @@ public class MapInfoWidgetsFactory { } } else if (map.getMapViewTrackingUtilities().isMapLinkedToLocation() && settings.SHOW_STREET_NAME.get()) { - streetName = new RoutingHelper.CurrentStreetName(); + streetName = new CurrentStreetName(); RouteDataObject rt = locationProvider.getLastKnownRouteSegment(); if (rt != null) { Location lastKnownLocation = locationProvider.getLastKnownLocation(); - streetName.text = RoutingHelper.formatStreetName( + streetName.text = RoutingHelperUtils.formatStreetName( rt.getName(settings.MAP_PREFERRED_LOCALE.get(), settings.MAP_TRANSLITERATE_NAMES.get()), rt.getRef(settings.MAP_PREFERRED_LOCALE.get(), settings.MAP_TRANSLITERATE_NAMES.get(), rt.bearingVsRouteDirection(lastKnownLocation)), rt.getDestinationName(settings.MAP_PREFERRED_LOCALE.get(), settings.MAP_TRANSLITERATE_NAMES.get(), rt.bearingVsRouteDirection(lastKnownLocation)), diff --git a/OsmAnd/src/net/osmand/plus/wikipedia/WikiArticleHelper.java b/OsmAnd/src/net/osmand/plus/wikipedia/WikiArticleHelper.java index 9caf70d5b9..25a7584f89 100644 --- a/OsmAnd/src/net/osmand/plus/wikipedia/WikiArticleHelper.java +++ b/OsmAnd/src/net/osmand/plus/wikipedia/WikiArticleHelper.java @@ -46,7 +46,7 @@ public class WikiArticleHelper { private static final String PAGE_PREFIX_HTTP = "http://"; private static final String PAGE_PREFIX_HTTPS = "https://"; private static final String PAGE_PREFIX_FILE = "file://"; - private static final String WIKIVOAYAGE_DOMAIN = ".wikivoyage.org/wiki/"; + public static final String WIKIVOYAGE_DOMAIN = ".wikivoyage.org/wiki/"; public static final String WIKI_DOMAIN = ".wikipedia.org/wiki/"; public static final String WIKI_DOMAIN_COM = ".wikipedia.com/wiki/"; @@ -245,7 +245,7 @@ public class WikiArticleHelper { } public static String getArticleNameFromUrl(String url, String lang) { - String domain = url.contains(WIKIVOAYAGE_DOMAIN) ? WIKIVOAYAGE_DOMAIN : + String domain = url.contains(WIKIVOYAGE_DOMAIN) ? WIKIVOYAGE_DOMAIN : url.contains(WIKI_DOMAIN) ? WIKI_DOMAIN : WIKI_DOMAIN_COM; String articleName = ""; diff --git a/OsmAnd/src/net/osmand/plus/wikipedia/WikipediaDialogFragment.java b/OsmAnd/src/net/osmand/plus/wikipedia/WikipediaDialogFragment.java index 3f2114327c..72ea3560cb 100644 --- a/OsmAnd/src/net/osmand/plus/wikipedia/WikipediaDialogFragment.java +++ b/OsmAnd/src/net/osmand/plus/wikipedia/WikipediaDialogFragment.java @@ -28,6 +28,7 @@ import androidx.appcompat.widget.PopupMenu; import androidx.appcompat.widget.Toolbar; import androidx.browser.customtabs.CustomTabsIntent; import androidx.core.content.ContextCompat; +import androidx.core.view.MotionEventCompat; import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentManager; @@ -123,20 +124,33 @@ public class WikipediaDialogFragment extends WikiArticleBaseDialogFragment { contentWebView = mainView.findViewById(R.id.content_web_view); contentWebView.setOnTouchListener(new View.OnTouchListener() { + float initialY, finalY; + boolean isScrollingUp; + @Override public boolean onTouch(View v, MotionEvent event) { - switch (event.getAction()) { - case MotionEvent.ACTION_SCROLL: - case MotionEvent.ACTION_MOVE: - readFullArticleButton.setVisibility(View.GONE); - break; - case MotionEvent.ACTION_CANCEL: - case MotionEvent.ACTION_DOWN: - case MotionEvent.ACTION_UP: - readFullArticleButton.setVisibility(View.VISIBLE); - break; + int action = event.getAction(); + + switch(action) { + case (MotionEvent.ACTION_DOWN): + initialY = event.getY(); + case (MotionEvent.ACTION_UP): + finalY = event.getY(); + if (initialY < finalY) { + isScrollingUp = true; + } else if (initialY > finalY) { + isScrollingUp = false; + } + default: } - return false; + + if (isScrollingUp) { + readFullArticleButton.setVisibility(View.VISIBLE); + } else { + readFullArticleButton.setVisibility(View.GONE); + } + + return false; } }); diff --git a/OsmAnd/src/net/osmand/plus/wikivoyage/WikivoyageWebViewClient.java b/OsmAnd/src/net/osmand/plus/wikivoyage/WikivoyageWebViewClient.java index ad3e348cbc..d5e678c61d 100644 --- a/OsmAnd/src/net/osmand/plus/wikivoyage/WikivoyageWebViewClient.java +++ b/OsmAnd/src/net/osmand/plus/wikivoyage/WikivoyageWebViewClient.java @@ -27,6 +27,7 @@ import net.osmand.plus.wikivoyage.explore.WikivoyageExploreActivity; import java.io.File; import java.util.List; +import static net.osmand.plus.wikipedia.WikiArticleHelper.WIKIVOYAGE_DOMAIN; import static net.osmand.plus.wikipedia.WikiArticleHelper.WIKI_DOMAIN; @@ -48,7 +49,6 @@ public class WikivoyageWebViewClient extends WebViewClient { private static final String PREFIX_GEO = "geo:"; private static final String PAGE_PREFIX_HTTP = "http://"; private static final String PAGE_PREFIX_HTTPS = "https://"; - private static final String WIKIVOAYAGE_DOMAIN = ".wikivoyage.org/wiki/"; private WikiArticleHelper wikiArticleHelper; @@ -64,11 +64,11 @@ public class WikivoyageWebViewClient extends WebViewClient { public boolean shouldOverrideUrlLoading(WebView view, String url) { url = WikiArticleHelper.normalizeFileUrl(url); boolean isWebPage = url.startsWith(PAGE_PREFIX_HTTP) || url.startsWith(PAGE_PREFIX_HTTPS); - if (url.contains(WIKIVOAYAGE_DOMAIN) && isWebPage) { + if (url.contains(WIKIVOYAGE_DOMAIN) && isWebPage) { String lang = WikiArticleHelper.getLang(url); String articleName = WikiArticleHelper.getArticleNameFromUrl(url, lang); String articleId = app.getTravelHelper().getArticleId(articleName, lang); - if (articleId.isEmpty()) { + if (!articleId.isEmpty()) { WikivoyageArticleDialogFragment.showInstance(app, fragmentManager, articleId, lang); } else { WikiArticleHelper.warnAboutExternalLoad(url, activity, nightMode);