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 79b3b8a349..3c3882af16 100644 --- a/OsmAnd-java/src/main/java/net/osmand/data/MapObject.java +++ b/OsmAnd-java/src/main/java/net/osmand/data/MapObject.java @@ -16,13 +16,11 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; -import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Map.Entry; -import java.util.Set; import java.util.zip.GZIPInputStream; @@ -183,7 +181,9 @@ public abstract class MapObject implements Comparable { public String getName(String lang, boolean transliterate) { if (lang != null && lang.length() > 0) { if (lang.equals("en")) { - return getEnName(transliterate); + // for some objects like wikipedia, english name is stored 'name' tag + String enName = getEnName(transliterate); + return !Algorithms.isEmpty(enName) ? enName : getName(); } else { // get name if (names != null) { diff --git a/OsmAnd-java/src/main/java/net/osmand/map/WorldRegion.java b/OsmAnd-java/src/main/java/net/osmand/map/WorldRegion.java index e3b2aa2a69..d54e4eb92c 100644 --- a/OsmAnd-java/src/main/java/net/osmand/map/WorldRegion.java +++ b/OsmAnd-java/src/main/java/net/osmand/map/WorldRegion.java @@ -5,8 +5,11 @@ import net.osmand.data.QuadRect; import net.osmand.util.Algorithms; import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashSet; import java.util.LinkedList; import java.util.List; +import java.util.Set; public class WorldRegion implements Serializable { @@ -212,4 +215,22 @@ public class WorldRegion implements Serializable { } return false; } + + public static List removeDuplicates(List regions) { + List copy = new ArrayList<>(regions); + Set duplicates = new HashSet<>(); + for (int i = 0; i < copy.size() - 1; i++) { + WorldRegion r1 = copy.get(i); + for (int j = i + 1; j < copy.size(); j++) { + WorldRegion r2 = copy.get(j); + if (r1.containsRegion(r2)) { + duplicates.add(r2); + } else if (r2.containsRegion(r1)) { + duplicates.add(r1); + } + } + } + copy.removeAll(duplicates); + return copy; + } } \ No newline at end of file diff --git a/OsmAnd-java/src/main/java/net/osmand/search/core/SearchCoreFactory.java b/OsmAnd-java/src/main/java/net/osmand/search/core/SearchCoreFactory.java index d00bf9ffb2..d720dc51ec 100644 --- a/OsmAnd-java/src/main/java/net/osmand/search/core/SearchCoreFactory.java +++ b/OsmAnd-java/src/main/java/net/osmand/search/core/SearchCoreFactory.java @@ -1317,7 +1317,13 @@ public class SearchCoreFactory { || !phrase.isSearchTypeAllowed(ObjectType.HOUSE)) { continue; } - res.localeName = b.getName(phrase.getSettings().getLang(), phrase.getSettings().isTransliterate()); + if(interpolation) { + res.localeName = lw; + res.location = b.getLocation(b.interpolation(lw)); + } else { + res.localeName = b.getName(phrase.getSettings().getLang(), phrase.getSettings().isTransliterate()); + res.location = b.getLocation(); + } res.otherNames = b.getAllNames(true); res.object = b; res.file = file; @@ -1328,12 +1334,6 @@ public class SearchCoreFactory { res.relatedObject = s; res.localeRelatedObjectName = s.getName(phrase.getSettings().getLang(), phrase.getSettings().isTransliterate()); res.objectType = ObjectType.HOUSE; - if(interpolation) { - res.location = b.getLocation(b.interpolation(lw)); - res.localeName = lw; - } else { - res.location = b.getLocation(); - } res.preferredZoom = 17; resultMatcher.publish(res); diff --git a/OsmAnd-java/src/main/java/net/osmand/search/core/SearchResult.java b/OsmAnd-java/src/main/java/net/osmand/search/core/SearchResult.java index 79d11a3fe0..60269fe5d4 100644 --- a/OsmAnd-java/src/main/java/net/osmand/search/core/SearchResult.java +++ b/OsmAnd-java/src/main/java/net/osmand/search/core/SearchResult.java @@ -55,7 +55,9 @@ public class SearchResult { public double getSumPhraseMatchWeight() { // if result is a complete match in the search we prioritize it higher - boolean match = requiredSearchPhrase.countWords(localeName) <= getSelfWordCount(); + int localWordsMatched = alternateName != null ? + requiredSearchPhrase.countWords(alternateName) : requiredSearchPhrase.countWords(localeName) ; + boolean match = localWordsMatched <= getSelfWordCount(); double res = ObjectType.getTypeWeight(match ? objectType : null); if (parentSearchResult != null) { res = res + parentSearchResult.getSumPhraseMatchWeight() / MAX_TYPE_WEIGHT; diff --git a/OsmAnd-java/src/test/java/net/osmand/search/SearchUICoreTest.java b/OsmAnd-java/src/test/java/net/osmand/search/SearchUICoreTest.java index f3ed25ca0b..07e7339401 100644 --- a/OsmAnd-java/src/test/java/net/osmand/search/SearchUICoreTest.java +++ b/OsmAnd-java/src/test/java/net/osmand/search/SearchUICoreTest.java @@ -80,9 +80,9 @@ public class SearchUICoreTest { if (files != null) { for (File file : files) { String fileName = file.getName(); - if(fileName.endsWith(".json")) { + if (fileName.endsWith(".json")) { String name = fileName.substring(0, fileName.length() - ".json".length()); - arrayList.add(new Object[] {name, file}); + arrayList.add(new Object[] { name, file }); } } } @@ -191,10 +191,10 @@ public class SearchUICoreTest { if (!Algorithms.stringsEqual(expected, present)) { System.out.println(String.format("Phrase: %s", phrase)); System.out.println(String.format("Mismatch for '%s' != '%s'. Result: ", expected, present)); + } for (SearchResult r : searchResults) { System.out.println(String.format("\t\"%s\",", formatResult(false, r, phrase))); } - } Assert.assertEquals(expected, present); } } diff --git a/OsmAnd-telegram/res/values-b+sr+Latn/strings.xml b/OsmAnd-telegram/res/values-b+sr+Latn/strings.xml new file mode 100644 index 0000000000..f281e65c4c --- /dev/null +++ b/OsmAnd-telegram/res/values-b+sr+Latn/strings.xml @@ -0,0 +1,274 @@ + + + Označi + Omogući + Nadmorska visina + Traži + U redu + Ažuriraj + Prosečna visina + Prosečna brzina + Karta + Dodaj + Sakrij + Stanje + Onemogući + Sačuvaj + Ime + Sortiraj + Izlaz + Zatvori + Sve + Isključeno + Instaliraj + Deli + Nazad + Nastavi + Otkaži + Postavke + Pozadinski režim + yd + ft + mi + km + m + nmi + min/m + min/km + m/s + km/h + mph + Kilometara na sat + Milja na sat + Metara u sekundi + Minuta po kilometru + Minuta po milji + Milje/stope + Milje/jardi + Kilometri/metri + Nautičke milje + Milje/metri + Primeni + Uključen + Merne jedinice & formatiranja + Promeni jedinice za dužinu. + Jedinice dužine + Izgled + Vremenska linija + Uživo sada + Moja lokacija + OsmAnd Pratioc omogućava vam da delite svoju lokaciju i vidite lokaciju drugih u OsmAndu.

Aplikacija koristi Telegram API, pa vam je potreban Telegram nalog.
+ sek + min + č + Nautičkih milja na sat (čvorovi) + nmi/č + Dobrodošli + Unesite vaš telefonski broj Telegrama u međunarodnom formatu + Autorizacija + Aktivna ćaskanja + Prikažite korisnike na mapi + Instalirajte OsmAnd + Prvo morate instalirati besplatnu ili plaćenu verziju OsmAnda + Logo OsmAnda + Usluga OsmAnd Pratioca + Deljenje lokacije + Deli lokaciju + Rastojanje + OsmAnd Pratioc radi u pozadini sa isključenim ekranom. + Izaberite jednog od dobavljača lokacije da bi deliti vašu lokaciju. + Uključite „Lokaciju“ u sistemskim podešavanjima + Aplikaciji nedostaje dozvola za pristup podacima o lokaciji. + Niste prijavljeni + Uključiti „Lokaciju“\? + Zatvaranje + Odjavljivanje + Pokretanje + Odjaviti se + Prijavite se + Telegram lozinka + Unesite lozinku + Telegram vam je poslao kod za OsmAnd radi prijave na vaš nalog. + Validacioni kod + Unesite kod + Lozinka + Broj telefona u međunarodnom formatu + Broj telefona + OsmAnd Onlajn GPS Pratioc + Prikaži na mapi + Deli lokaciju + Pretražite: Grupu ili kontakt + Izaberite kontakte i grupe sa kojima želite da delite lokaciju. + Podesite vreme + Podesite vreme za koji će izabrani kontakti i grupe videti vašu lokaciju u realnom vremenu. + Vidljivo vreme za sve + %1$ č + %1$ m + %1$ č %2$ m + Podesite vidljivo vreme za sve + Unesite kod za validaciju + Unesite broj telefona + Nemam Telegram nalog + Potreban vam je registrovani Telegram nalog i broj telefona + Tada možete da koristite ovu aplikaciju. + Instalirajte Telegram i otvorite nalog. + Za deljenje lokacije potreban vam je Telegram nalog. + Registracija u Telegramu + Bot + Uživo + Otvori OsmAnd + Isključite deljenje lokacije + Deljenje je uključeno (isključite) + Ističe + Vreme deljenja + Pozicija + Pošalji moju lokaciju + Podesite minimalni interval za deljenje lokacije. + Nepomičan + Poslednji put kada se kontakt pomerio. + Istorija lokacije + Sakrijte kontakte koji se nisu pomerili u datom vremenu. + Osmand veza + Odaberite verziju OsmAnda koju OsmAnd pratioc koristi za prikazivanje pozicija. + u %1$ + Nalog + Povezani nalog + Kako isključiti OsmAnd pratioca iz Telegrama + Kako isključiti OsmAnd pratioca iz Telegrama + Da biste opozvali pristup deljenju lokacije. Otvorite Telegram, idite na Podešavanja → Privatnost i bezbednost → Sesije i prekinete sesiju OsmAnd pratioca. + Povežite se na Internet kako biste se pravilno odjavili iz Telegrama. + Grupa + Poslednji odgovor + pre + Isključi sve + Onemogući svako deljenje + Isključuje deljenje lokacije prema svim izabranim čatovima (%1$). + Izaberite verziju OsmAnda koju želite da koristite + Izaberite verziju OsmAnda gde će se kontakti prikazati na mapi. + Sortiraj po + Po grupi + Po imenu + Po udaljenosti + Odjaviti se sa OsmAnd pratioca\? + Jeste li sigurni da se želite odjaviti sa OsmAnd pratioca tako da ne možete da delite lokaciju ili vidite lokaciju drugih\? + Kontakti i grupe dele lokaciju vama. + Deljenje lokacije kao + Dodajte uređaj + Nema internet konekcije + Nema GPS veze + Deljenje: %1$ + Deljenje statusa + Poslednja dostupna lokacija + Ponovo pošalji lokaciju + Još nije pronađeno + Još nije poslato + Kasnije + Idi na Podešavanja + Deljenje u pozadini + Isključite optimizaciju baterije za OsmAnd pratilac tako da se ne isključi iznenada kad je u pozadini. + Rad u pozadini + Promenite podešavanja za optimizaciju baterije da biste stabilizovali deljenje lokacije. + Povezivanje sa Internetom + Pozicioniranje… + Pokretanje + Lokacija se šalje + Čeka se odgovor iz Telegrama + Nije moguće poslati u Telegram četove: + Uspešno poslato i ažurirano + Poslednja ažurirana lokacija: + Ako želite da povežete više uređaja sa jednim nalogom telegrama, trebate koristiti drugi uređaj da bi delili vašu lokaciju. + Možete da kreirate i vidite ID uređaja u telegram klijentu koristeći %1$ čat bot. %2$ + Ime uređaja + Ime uređaja ne može biti prazno + Ime uređaja predugo + Imenujte vaš novi uređaj sa maksimalno 200 simbola. + Nije moguće dodati novi uređaj + %1$ dodato. + Izaberite ime koje niste već koristili + Poslednje ažuriranje od Telegrama + Mapa i tekst + Tekst + Odaberite kako će izgledati poruke sa vašom lokacijom. + Pošalji lokaciju kao + Početni datum + Krajnji datum + Prikaži u OsmAndu + Vreme kretanja + Praćenje je onemogućeno + Praćenje je omogućeno + Poslato + GPS tačke + Prikupljeno + Datum + %1$ tačaka + poslato (%1$ u baferu) + Ažurirajte OsmAnd da biste videli podatke na mapi + Prikaži količinu prikupljenih i poslatih GPS tačaka. + Pokaži GPS tačke + Primljene GPKS tačke: %1$ + Kako radi + OsmAnd politika privatnosti + Politika privatnosti Telegrama + Prihvati + Klikom na „Nastavi“ prihvatate uslove politike privatnosti Telegrama i OsmAnda. + OsmAnd pratilac je jedan od klijenata koji koriste otvorenu platformu Telegram. Vaši kontakti mogu da koriste bilo koji drugi Telegram klijent. + Telegram (aplikacija za razmenu poruka) koristi se za povezivanje i komunikaciju sa ljudima. + Telegram + OsmAnd pratilac + Omogućite praćenje da biste sačuvali sve lokacije u istoriji. + Snimanje lokacije omogućeno + Onemogućite praćenje + Vremenska linija je funkcija koja je sada dostupna besplatno. + Unesite ime kontakta ili grupe + Pretraga po svim vašim grupama i kontaktima. + Pretraga kontakta + Usmerenje + Preciznost + Smer + Privatnost + Proksi + Podešavanja proksija + Prekinut + Povezan + Tip proksija + Veza + Server + Port + Akreditivi + Korisničko ime + Lozinka + Ključ + GPX podešavanja + Filter: nema zapisivanja ispod odabrane brzine + Minimalna brzina zapisivanja + Filter: Nema zapisa dok se ne dostigne ova tačnost + Minimalna tačnost evidentiranja + Filter: minimalna udaljenost za evidentiranje nove tačke + Minimalna udaljenost evidentiranja + Nema podataka + Nemamo prikupljene podatke za izabrani dan + Početni — Krajnji datum + Izaberite vreme za prikaz + Početak + Kraj + Sačuvane poruke + Jedinica brzine + Definišite jedinicu brzine. + Vremenska zona + Izaberite vremensku zonu koja će se prikazati u porukama lokacije. + Vreme isteka bafera + Maksimalno vreme za skladištenje tačaka u bafer + Status Tragača OsmAnda + Predloženo + Povratak na OsmAnd + Pre %1$ + Poslednji odgovor: pre %1$ + Poslednje ažuriranje iz Telegrama: pre %1$ + Poslednji odgovor: %1$ + Poslednje ažuriranje iz Telegrama: %1$ + Greška + Izvezi + Logcat bafer + Proverite i podelite detaljne zapise aplikacije + Pošalji izveštaj +
diff --git a/OsmAnd-telegram/res/values-eo/strings.xml b/OsmAnd-telegram/res/values-eo/strings.xml index b902b91d73..980f304973 100644 --- a/OsmAnd-telegram/res/values-eo/strings.xml +++ b/OsmAnd-telegram/res/values-eo/strings.xml @@ -119,4 +119,28 @@ Konektante al Interreto Komencante Sendante lokon + Pasvorto de Telegram + Tajpu pasvorton + Montri sur mapo + %1$d h + %1$d min + %1$d h %2$d min + Sendita + Vi ne estas salutinta + Fermante + Adiaŭante + Lanĉante + Adiaŭi + Saluti + Fona reĝimo + Malaktiva + Ĉiuj + Fermi + Eliri + Konservi + Malaktivigi + Apliki + Ŝanĝi unuojn por reprezenti distancoj. + Bufro logcat + Legi kaj kunhavigi detalajn protokolojn de la aplikaĵo \ No newline at end of file diff --git a/OsmAnd-telegram/res/values-hu/strings.xml b/OsmAnd-telegram/res/values-hu/strings.xml index 95d655b5cb..01544a2b89 100644 --- a/OsmAnd-telegram/res/values-hu/strings.xml +++ b/OsmAnd-telegram/res/values-hu/strings.xml @@ -109,7 +109,7 @@ Kérjük, telepítse a Telegramot és hozzon létre egy fiókot. Utána használhatja ezt az alkalmazást. Minden - Kikapcsolás + Kikapcsolva %1$d óra %2$d perc %1$d perc Telepítés diff --git a/OsmAnd-telegram/res/values-ja/strings.xml b/OsmAnd-telegram/res/values-ja/strings.xml index 1829c1fcc9..57bf1c209e 100644 --- a/OsmAnd-telegram/res/values-ja/strings.xml +++ b/OsmAnd-telegram/res/values-ja/strings.xml @@ -22,7 +22,7 @@ ポート サーバー 接続 - 有効 + 有効化 プロキシタイプ 接続しました 切断しました diff --git a/OsmAnd/res/drawable/rectangle_rounded_left.xml b/OsmAnd/res/drawable/rectangle_rounded_left.xml new file mode 100644 index 0000000000..56b3235c6c --- /dev/null +++ b/OsmAnd/res/drawable/rectangle_rounded_left.xml @@ -0,0 +1,7 @@ + + + + diff --git a/OsmAnd/res/drawable/ripple_rectangle_rounded_left.xml b/OsmAnd/res/drawable/ripple_rectangle_rounded_left.xml new file mode 100644 index 0000000000..c63df94daf --- /dev/null +++ b/OsmAnd/res/drawable/ripple_rectangle_rounded_left.xml @@ -0,0 +1,11 @@ + + + + + + + + \ No newline at end of file diff --git a/OsmAnd/res/drawable/seekbar_progress_announcement_time.xml b/OsmAnd/res/drawable/seekbar_progress_announcement_time.xml deleted file mode 100644 index 794539923e..0000000000 --- a/OsmAnd/res/drawable/seekbar_progress_announcement_time.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/OsmAnd/res/drawable/seekbar_thumb_announcement_time.xml b/OsmAnd/res/drawable/seekbar_thumb_announcement_time.xml deleted file mode 100644 index a126aee222..0000000000 --- a/OsmAnd/res/drawable/seekbar_thumb_announcement_time.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/OsmAnd/res/drawable/seekbar_tickmark_announcement_time.xml b/OsmAnd/res/drawable/seekbar_tickmark_announcement_time.xml deleted file mode 100644 index d6662da81f..0000000000 --- a/OsmAnd/res/drawable/seekbar_tickmark_announcement_time.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - \ No newline at end of file diff --git a/OsmAnd/res/layout-land/fragment_measurement_tool.xml b/OsmAnd/res/layout-land/fragment_measurement_tool.xml index 505e680262..b99a679f40 100644 --- a/OsmAnd/res/layout-land/fragment_measurement_tool.xml +++ b/OsmAnd/res/layout-land/fragment_measurement_tool.xml @@ -16,20 +16,34 @@ android:focusable="true" android:orientation="vertical" android:clickable="true" - tools:background="@drawable/bg_bottom_menu_dark"> + tools:ignore="UselessParent"> - + android:layout_height="wrap_content"> + + + + + + + android:layout_height="match_parent" + android:background="?attr/list_background_color"> + + diff --git a/OsmAnd/res/layout/bottom_sheet_announcement_time.xml b/OsmAnd/res/layout/bottom_sheet_announcement_time.xml index 246ffe3476..308ad0278a 100644 --- a/OsmAnd/res/layout/bottom_sheet_announcement_time.xml +++ b/OsmAnd/res/layout/bottom_sheet_announcement_time.xml @@ -62,18 +62,12 @@ osmand:typeface="@string/font_roboto_medium" tools:text="Normal" /> - + android:layout_marginLeft="@dimen/content_padding" + android:layout_marginRight="@dimen/content_padding" /> + + + + + + + \ No newline at end of file diff --git a/OsmAnd/res/layout/custom_radio_btn_icon_item.xml b/OsmAnd/res/layout/custom_radio_btn_icon_item.xml new file mode 100644 index 0000000000..8a54559283 --- /dev/null +++ b/OsmAnd/res/layout/custom_radio_btn_icon_item.xml @@ -0,0 +1,20 @@ + + + + + + \ No newline at end of file diff --git a/OsmAnd/res/layout/dialog_list_item_with_compound_button.xml b/OsmAnd/res/layout/dialog_list_item_with_compound_button.xml index 53ca8d39a7..809d19fabc 100644 --- a/OsmAnd/res/layout/dialog_list_item_with_compound_button.xml +++ b/OsmAnd/res/layout/dialog_list_item_with_compound_button.xml @@ -2,6 +2,7 @@ + tools:text="Item title"/> diff --git a/OsmAnd/res/layout/drawer_list_radius_ex.xml b/OsmAnd/res/layout/drawer_list_radius_ex.xml index 4c84dacfd3..de6fb3fd54 100644 --- a/OsmAnd/res/layout/drawer_list_radius_ex.xml +++ b/OsmAnd/res/layout/drawer_list_radius_ex.xml @@ -1,13 +1,14 @@ @@ -69,7 +70,7 @@ android:textColor="@color/color_myloc_distance" android:layout_gravity="center_vertical" android:layout_marginTop="2dp" - android:text="Cinema" + tools:text="Cinema" android:layout_marginRight="16dp" android:layout_marginEnd="16dp" /> diff --git a/OsmAnd/res/layout/fragment_measurement_tool.xml b/OsmAnd/res/layout/fragment_measurement_tool.xml index 71bf4671fe..e0cc888b63 100644 --- a/OsmAnd/res/layout/fragment_measurement_tool.xml +++ b/OsmAnd/res/layout/fragment_measurement_tool.xml @@ -13,293 +13,314 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="bottom" - android:background="@drawable/bg_bottom_menu_dark" android:orientation="vertical" android:clickable="true" - android:focusable="true"> - - - - - - - - - - - - - - - - - - - + android:focusable="true" + tools:ignore="UselessParent"> - + android:layout_height="0dp" + android:background="@drawable/bg_contextmenu_shadow_top_light" /> + + - - - - + android:layout_height="wrap_content" + android:background="?attr/list_background_color" + android:orientation="vertical"> - + - + + + + + + + + - + android:textColor="?android:textColorSecondary" + android:textSize="@dimen/default_desc_text_size" + tools:text=" – 700 m" /> - + - + + + android:id="@+id/info_type_buttons_container" + android:layout_width="match_parent" + android:layout_height="wrap_content"> + layout="@layout/custom_radio_buttons" + android:layout_width="match_parent" + android:layout_height="@dimen/measurement_tool_button_height" + android:layout_marginStart="@dimen/content_padding" + android:layout_marginEnd="@dimen/content_padding" + android:layout_marginBottom="@dimen/measurement_tool_content_padding_medium" /> - - - - - - - - - - - - + + android:id="@+id/bottom_panel_divider" + android:layout_width="match_parent" + android:layout_height="1dp" + android:background="?attr/dashboard_divider" /> - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OsmAnd/res/layout/measurement_tool_graph_card.xml b/OsmAnd/res/layout/measurement_tool_graph_card.xml index fecbe7ae89..aebc1554a7 100644 --- a/OsmAnd/res/layout/measurement_tool_graph_card.xml +++ b/OsmAnd/res/layout/measurement_tool_graph_card.xml @@ -4,6 +4,9 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" + android:background="?attr/list_background_color" + android:clickable="true" + android:focusable="true" android:orientation="vertical"> @@ -32,7 +33,7 @@ android:id="@+id/user_name_field" android:layout_width="fill_parent" android:layout_height="wrap_content" - android:text="NoName"/> + tools:text="NoName"/> + xmlns:app="http://schemas.android.com/apk/res-auto" + android:layout_width="match_parent" + android:layout_height="match_parent" + xmlns:osmand="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:orientation="vertical"> diff --git a/OsmAnd/res/layout/settings_group_title.xml b/OsmAnd/res/layout/settings_group_title.xml index 7d4fb811cd..6c2c220b4e 100644 --- a/OsmAnd/res/layout/settings_group_title.xml +++ b/OsmAnd/res/layout/settings_group_title.xml @@ -31,7 +31,7 @@ tools:text="Some title" /> + + + + + + + android:paddingBottom="@dimen/content_padding_small"> diff --git a/OsmAnd/res/layout/subscription_fragment.xml b/OsmAnd/res/layout/subscription_fragment.xml index ad308e6be1..5deb8207c2 100644 --- a/OsmAnd/res/layout/subscription_fragment.xml +++ b/OsmAnd/res/layout/subscription_fragment.xml @@ -2,6 +2,7 @@ xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" + xmlns:tools="http://schemas.android.com/tools" android:background="?attr/spinnerListBackground" android:orientation="vertical"> @@ -212,7 +213,7 @@ android:paddingLeft="2dp" android:paddingEnd="0dp" android:paddingRight="0dp" - android:text="Ukraine" + tools:text="Ukraine" app:drawableEndCompat="@drawable/ic_action_arrow_drop_down" app:drawableRightCompat="@drawable/ic_action_arrow_drop_down" /> diff --git a/OsmAnd/res/layout/subscription_layout.xml b/OsmAnd/res/layout/subscription_layout.xml index 411e49db31..044ab6ba03 100644 --- a/OsmAnd/res/layout/subscription_layout.xml +++ b/OsmAnd/res/layout/subscription_layout.xml @@ -64,7 +64,6 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="@dimen/content_padding_small" - android:layout_gravity="center_vertical" android:paddingStart="@dimen/content_padding_small" android:paddingLeft="@dimen/content_padding_small" android:paddingTop="@dimen/content_padding_small_half" diff --git a/OsmAnd/res/layout/subscriptions_card.xml b/OsmAnd/res/layout/subscriptions_card.xml index 4635957ad7..7d2058a487 100644 --- a/OsmAnd/res/layout/subscriptions_card.xml +++ b/OsmAnd/res/layout/subscriptions_card.xml @@ -1,8 +1,6 @@ - + - - - - - - - - - - - - - - - - - - - + diff --git a/OsmAnd/res/layout/switch_select_list_item.xml b/OsmAnd/res/layout/switch_select_list_item.xml index d362ff05f2..7c76fc5dec 100644 --- a/OsmAnd/res/layout/switch_select_list_item.xml +++ b/OsmAnd/res/layout/switch_select_list_item.xml @@ -1,14 +1,14 @@ - @@ -36,7 +36,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center_vertical" - android:text="Avoid selected roads" + tools:text="Avoid selected roads" android:textSize="@dimen/default_list_text_size"/> diff --git a/OsmAnd/res/layout/test_backup_layout.xml b/OsmAnd/res/layout/test_backup_layout.xml index 216820a9bf..ffdd50e2aa 100644 --- a/OsmAnd/res/layout/test_backup_layout.xml +++ b/OsmAnd/res/layout/test_backup_layout.xml @@ -42,6 +42,7 @@ @@ -117,10 +118,17 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="@dimen/content_padding" - android:text="Actions" + tools:text="Actions" android:textColor="?android:textColorPrimary" android:textSize="@dimen/default_list_text_size" /> + + + android:layout_width="fill_parent" + android:layout_height="fill_parent" + xmlns:tools="http://schemas.android.com/tools" + android:background="@drawable/popup_bg" + android:gravity="center" + android:orientation="horizontal"> \ No newline at end of file diff --git a/OsmAnd/res/values-ar/strings.xml b/OsmAnd/res/values-ar/strings.xml index e920c24f05..5f81b828e0 100644 --- a/OsmAnd/res/values-ar/strings.xml +++ b/OsmAnd/res/values-ar/strings.xml @@ -4123,7 +4123,11 @@ خط المسار سيستخدم خط الطريق %1$s المحدد في نمط الخريطة المحدد: %2$s. حدد لونًا لوضع الخريطة: %1$s. - • تم نقل تحديثات OsmAnd Live إلى \"التنزيلات> التحديثات\" + • خيار مضاف لتنزيل خطوط الكنتور بالقدم +\n +\n• مخطط الطريق الأفقي: علامات تبويب tabs مضافة للتبديل بين النقاط أو الرسوم البيانية +\n +\n• تم نقل تحديثات OsmAnd Live إلى \"التنزيلات> التحديثات\" \n \n • يمكن تلوين المسارات الآن حسب الارتفاع، السرعة أو المنحدر. \n @@ -4154,4 +4158,8 @@ الإعلان عند التجاوز نقاط المستخدم الإخراج + تنسيق وحدة خطوط الكنتور + OsmAnd يوفر بيانات الخطوط الكنتورية بالأمتار والقدم. ستحتاج إلى إعادة تنزيل الملف لتغيير التنسيق. + الرجاء تحديد التنسيق المطلوب. ستحتاج إلى إعادة تنزيل الملف لتغيير التنسيق. + قدم \ No newline at end of file diff --git a/OsmAnd/res/values-b+sr+Latn/phrases.xml b/OsmAnd/res/values-b+sr+Latn/phrases.xml new file mode 100644 index 0000000000..311b4a1425 --- /dev/null +++ b/OsmAnd/res/values-b+sr+Latn/phrases.xml @@ -0,0 +1,3911 @@ + + + Vrsta goriva + Način plaćanja + Platežne kartice za gorivo + Dodatno + Vrsta pristupa internetu + Način prodaje + Održavanje bicikala + Vrsta + Vrsta + Vrsta + Položaj + Izvor vode + Način plaćanja + Zvuk + Vrsta + Održavanje + Sadržaj + Dodatno + Kamp izviđača + Tip + Težina staze + Ravnanje staze + Rod + Spoljašnja klupa + Naknada + Pušenje + Dostava + Drajv-in + Za poneti + Kokteli + Mikropivara + Tip + Dozvoljeni otpad + Tip + Kamin + Sezonski + Osobine vode + Podloga + Nudizam + Tip masaže + Šatori + Mašina za pranje veša + Karavani + Medicinski sistem + Kućna poseta + Tip + Ciljna grupa + Vazduh pod pritiskom + Usisivač + Samousluga + Automatizovano + Vrsta + Prekriven + Podzemna stanica + Teret + Vrsta + Prevoz biciklom + Grejanje + Pumpa + Vrsta + Vrsta + TipVrsta + Glavni grad + Specijalizacija + Osobina + Vrsta + Broj zvezdica + Veroispovest + Vrsta + Drajv tru + Jelo + Tip plaćanja (prevoz) + Osobina + Kafić + Tip + Tip motocikla + Dozvoljene životinje + Svrha + Iznajmljivanje čamaca + Prečišćavanje vode + Stil penjanja + Utičnica + Auto servis + Tip glečera + Prevoz + Prepreka na putu + Benzinska stanica + Lični prevoz + Javni prevoz + Vazdušni prevoz + Vodeni prevoz + Prevoz biciklom + Prevoz vazdušnim putem + Rute za pešačenje + Struja + Komunikacije + Odlaganje smeća + Obrazovanje + Administracija + Zdravstvo + Kancelarije + Sport + Turizam + Razgledanje okoline + Pristup internetu + Klub + Hrana + Kafić i restoran + Finansije + Priroda + Pomorska + Vojska + Vikipedija + Korisnički definisano + Pekara + Podrum pića + Prodavnica sira + Prodavnica čokolade + Prodaje kafe + Tržni centar + Prodavnica pića + Mesna pijaca + Stočna prodavnica + Prodavnica morske hrane + Konfekcija + Supermarket + Prodavnica čaja + Prodavnica testenina + Vinski podrum + Knjižara + Prodavnica bickli + Anime prodavnica + Antikvarnica + Prodavnica umetnina + Stvari za bebu + Modni butik + Fotoaparati i sočiva + Prodavnica tepiha + Drogerija + Prodavnica odeće + Prodavnica odeće za decu + Prodavnica cipela + Prodavnica sveža + Prodavnica računara + Fotokopirnica + Prodavnica zavesa + Prodavnica tkanine + Vrata + Seks šop + Modna prodavnica + Cvećara + Prodavnica ramova + Prodavnica poklona + Oprema za lov + Juvelir + Kiosk + Prodavnica kože + Prodavnica mobilnih telefona + Prodavnica motocikala + Muzička radnja + Muzički instrumenti + Novinarnica + Optometrija + Organski proizvod + Prodavnica boja + Prodavnica kućnih ljubimaca + Foto radnja + Prodavnica polovnih stvari + Sportska oprema + Prodaja karata + Prodavnica duvana + Prodavnica igračaka + Prodavnica guma + Prodavnica usisivača + Video klub + Prodavnica elektronike + Auto delovi + Kozmetika + CHAdeMO izlaz + Tip 2 izlaz + Tip 2 kombo izlaz + Tip 3 izlaz + CEE plavi izlaz + Šuko izlaz + Paleontološki lokalitet + Prodavnica tečnog gasa + Slušni aparati + HiFi prodavnica + Prodavnica enterijera + Prodavnica opreme za ronjenje + Prodavnica osušene robe + Sajdžija + Prodavnica opreme za bazene + Igre + Trofeji, nagrade + Video igrice + Pirotehnika + Prodavnica oružja + Parfimerija + Prodavnica začina + Prodavnica meda + Policija + Vatrogasci + Hidrant + Vatrogasno crevo + Ambulanta + SES stanica + Tačka za okupljanje u slučaju nužde + Zaliv + Planinski prolaz + Kapija + Gradske zidine + Kapija lifta + Telefonska govornica + Pasoška kontrola + Ležeći policajac + Jastuče (brzinsko) + Šikana + Saobraćajno ostrvo + Stop svetlo + Popravka kola + Gume + Pregled vozila + Pranje kola + Benzinska pumpa;Pumpa za plin;Stanica za punjenje + Dizel + GTL dizel + HGV dizel + Biodizel + LPG + Oktan 80 + Oktan 91 + Oktan 92 + Oktan 95 + Oktan 98 + Oktan 100 + 1:25 gorivo + 1:50 gorivo + Etanol + Metanol + SVO + E10 + E20 + E85 + Biogas + Tečni vodonik + Struja + Benzinska stanica za letelice + Benzinska stanica za brodove + Vazduh pod pritiskom + Parking + Parking za motocikle + Ulaz u parking + Garaže + Stanica javnog prevoza + Autobuska stanica + Stanica trolejbusa + Autobuska stanica + Tramvajska stanica + Tramvajska stanica + Stanica javnog prevoza + Autobuska stanica + Železnička stanica + Ulaz u podzemnu + Da + Taksi stanica + Aerodrom + Heliodrom + Pista + Vazdušni terminal + Vazdušni gejt + Svetionik + Iznajmljivanje bicikli + Parking za bicikle + Gondola + Sadržaj: usev + Usev: pirinač + Usev: trava + Usev: kukuruz + Usev: žitarice + Usev: šećerna trska + Usev: pšenica + Usev: soja + Usev: ječam + Usev: povrće + Usev: hmelj + Usev: repica + Usev: cvekla + Usev: lavanda + Usev: čaj + Usev: kafa + Usev: živina + Usev: suncokret + Usev: malina + Usev: duvan + Usev: jagoda + Usev: povrće, cveće + Usev: tapioka + Usev: brusnica + Usev: špargla + Usev: cveće + Da + Tuš: da + Tuš: ne + Tuš: vruć + Tuš: spolja + Tuš: hladan + Tuš: unutra + Da + Nekropola + Izlazna snaga + Izlazna snaga tople vode + Izlazna snaga vazduha pod pritiskom + Protivpožarni aparat + CNG + Stanica za punjenje + Rampa za vozila + Trajektni terminus + Žičara + Lift za robu + Uspinjača + Tunel + Most + Radar kamera + Raskrsnica + Odmorište + Bunar sa vodom + Radovi na vodi + Vodeni toranj + Kapija sa zaključavanjem + Brana + Vodenica + Razbijač talasa + Transformator + Elektrana + Strujni generator + Pošta + Poštansko sanduče + Telefon + Komunikacioni toranj + Osmatračnica + Reciklaža + Centar za reciklažu + Kontejner + Staklo + Papir + Odeća + Limenke + Staklene flaše + Plastika + Metalni otpaci + Baterije + Plastične flaše + Zelenilo + Otpad (crne kese) + Plastične ambalaže + Novine + Karton + Kartonske kutije + Časopisi + Papirnate ambalaže + Mali uređaji + Drvo + Knjige + Cipele + Aluminijum + Organsko + Baštenski otpad + Sijalice male snage + Fluorescentne sijalice + Metal + Električne stvari + Ulje za kuvanje + Motorno ulje + Plastične kese + Opasni otpad + Mobilni telefoni + Živa + Računari + Gume + TV, monitori + Tetrapak + Krš + CD-ovi + Otpadno ulje + Flaše + Pluta + Kertridži iz štampača + Boja + Stiroform + Lekovi + Kompost + Novogodišnje jelke + Sijalice + Poliester + Životinjski otpad + Frižideri i zamrzivači + Nameštaj + Pelene + Akumulatori + Automobili + Bicikli + Nuklearni otpad + Odlaganje smeća + Kanta za smeće + Stambena oblast + Industrijska oblast + Železnička oblast + Trgovinsko zemljište + Zemljište namenjeno za maloprodaju + Kamenolom + Vinograd + Voćnjak + Bašte + Seosko dvorište + Livada + Sliv + Kanal + Dok + Nadzor + Opservatorija + Astronomska + Toranj + Jarbol + Radar + Kran + Radovi + Vetrenjača + Rezervoar + Silos + Koledž + Vozačka škola + Vrtić + Škola + Ustanova za vežbanje + Univerzitet + Sud + Zatvor + Ambasada + Tužilac + Penzioni fond + Carina + Država + Grad + Da + Varošica + Selo + Zaseok + Predgrađe + Lokalitet + Farma + Apoteka + Bolnica + Doktori + Klinika + Prva pomoć + Stomatolog + Veterinar + Alternativna medicina + Banka krvi + Optometrist + Fizioterapeut + Psihoterapeut + Rehabilitacija + Medicinska ustanova + Pedijatrija + Kompanija + Osiguranje + Agent za nekretnine + Advokat + NVO + Gradska većnica + IT kancelarija + Marketinška agencija + Obrazovna ustanova + Studio + Prostorije stranke + Notar + Stadion + Sportski centar + Golf teren + Sportska staza + Trkačka staza + Biciklistička staza + Konjska staza + Trkalište + Kuglanje sa 9 čunjeva + Kuglanje sa 10 čunjeva + Streljaštvo + Atletika + Američki fudbal + Australijski fudbal + Bejzbol + Košarka + Odbojka na pesku + Kanadski fudbal + Šah + Gromobran + Telefonska centrala + Kartoni od pića + Bela tehnika + Lim + Folija + Ljudski održavana šuma + Stanica za nadgledanje + Građevina + Šaht + Slana bara + Gaosmetar + Bunker silos + Tank za skladištenje + Socijalna ustanova + Državna uprava + Sudski izvršitelj + Migraciono + Poreska inspekcija + Administracija + Izolovano mesto stanovanja + Četvrt + Komšiluk + Dom za stare + Sanatorijum + Audolog + Babica + BMX + Kanu + Penjanje + Kriket + Kroket + Biciklizam + Ronjenje + Skuba ronjenje + Trke pasa + Hokej na travi + Hokej na ledu + Golf + Gimnastika + Rukomet + Hokej + Trke konja + Skijanje na ledu + Korfbol + Moto trke + Više sportova + Orijentiring + Paraglajding + Pelote + Reket + Roleri + Veslanje + Ragbi unija + Ragbi liga + Jedrenje + Klizanje + Skejtbord + Skijanje + Fudbal + Surfovanje + Plivanje + Stoni tenis + Tenis + Tobogan + Odbojka + Muzej + Memorijalni spomenik + Arheološko nalazište + Istorijski top + Zamak + Gradska kapija + Tvrđava + Fontana + Istorijske ruševine + Brodska olupina + Istorijski brod + Istorijska mina + Spomenik + Zoološki vrt + Akvarijum + Tematski park + Turistička atrakcija + Turistički objekat + Životinja (atrakcija) + Vrteška + Lavirint + Rolerkoster + Spust + Letnji tobogan + Balerina + Voz (atrakcija) + Vodeni spust + Smeštaj + Hotel + Gostinska kuća + Hostel + Motel + Planinska brvnara + Stan + Brvnara u divljini + Hrišćanstvo + Judaizam + Islam + Sikhizam + Budizam + Hinduizam + Šinto + Taoizam + Vudu + Džainizam + Spiritualizam + Bahaizam + Sajentologija + Paganizam + Zoroastrijanizam + Katolicizam + Baptizam + Rimokatolicizam + Pravoslavlje + Suni + Protestantizam + Metodizam + Rusko pravoslavlje + Mormonstvo + Jehovini svedoci + Grčko pravoslavlje + Adventisti sedmog dana + Engleska crkva + Škotska crkva + Armija spasa + Kvaker + Bugarsko pravoslavlje + Adventisti + Srpsko pravoslavlje + Crkva Isusa Hrista + Jermensko pravoslavlje + Rumunsko pravoslavlje + Mahajana + Internet pristup: žični + Internet pristup: javni + Internet pristup: da + Internet pristup: ne + Internet pristup: sa naknadom + Internet pristup: bez naknade + Manastir + Informacije + Sat + Turistički agent + Vidikovac + Mesto za piknik + Sto za piknik + Izvor + Vrelo + Gejzir + Groblje + Grobnica + Sklonište + Podrum vina + Vinarija + Astronomski klub + Kompjuterski klub + Motociklistički klub + Sportski klub + Igrački klub + Auto klub + Šahovski klub + Ribolovački klub + Klub ratnih veterana + Klub linuksaša + Kampiralište + Odmaralište + Klub ljubitelja + Pozorišni klub + Klub istoričara + Muzički klub + Etnički klub + Klub ljubitelja prirode + Klub fotografa + Lovački klub + Streličarski klub + Turistički klub + Masonska loža + Klub jedriličara + Izviđački klub + Staza + Staza nizbrdo + Nordijska staza + Pešačka staza + Snežni park + Staza za igranje + Klizalište + Najam skija + Biblioteka + Bioskop + Kazino + Socijalni centar + Pozorište + Cirkus + Umetnička galerija + Podijum za igru + Noćni klub;Disko klub + Striptiz + Ski omdaralište + Morsko odmaralište + Park za pse + Mesto za pecanje + Luka + Marina + Mini golf + Igralište + Klupa + Bazen za plivanje + Akva park + Park + Teren za rekreaciju + Kafić + Pivska bašta + Restoran + Brza hrana + Bar + Pab + Voda za piće + Roštilj + Korpar + Pivnica + Brodogradioc + Ćilimar + Sajdžija + Električar + Baštovan + Ručni radovi + Grejanje, ventilacija, klima + Izolacija + Optičar + Slikar + Parketar + Fotograf + Vodoinstalater + Mlinar + Skulptor + Obućar + Krojač + Časovničar + Izrada prozora + Upravnik sahrana + Salon lepote + Manikir + Frizer + Salon za masažu + Tetovaže + Pranje veša + Iznajmljivanje kola + Deljenje kola + Deljenje brodova + Dok + Tuš + Sauna + Mrtvačnica + Krematorijum + Internet kafe + Banka + Terminal za plaćanje + Novčane pozajmice + Menjačnica + Računovođa + Bitkoin plaćanje + Ulaz u pećinu + Planinski vrh + Sedlo + Vulkan + Krater + Greben + Glečer + Vrtača + Vodopad + Reka + Potok + Brzaci + Rt + Plaža + Zaliv + Fjord + Greben + Dolina + Voda + Tresetište + Drvo + Stablo + Prirodni rezervat + Moreuz + Ostrvo + Hrid + Instalaciona bova + Bova, izolovana opasnost + Lateralna bova + Bova sigurnih voda + Bova specijalne namene + Suvi dok + Plutajući dok + Lučki bazen + Luka + Vojna oblast + Vojno sklonište + Barake + Opasna zona + Vojne kancelarije + Vojna mornarička baza + Mesto nuklearne eksplozije + Vikipedija + Engleska vikipedija + Arapska vikipedija + Beloruska vikipedija + Bugarska vikipedija + Katalonska vikipedija + Kebuanska vikipedija + Češka vikipedija + Danska vikipedija + Nemačka vikipedija + Grčka vikipedija + Estonska vikipedija + Španska vikipedija + Finska vikipedija + Francuska vikipedija + Galicijska vikipedija + Jevrejska vikipedija + Hindi vikipedija + Hrvatska vikipedija + Haićanska vikipedija + Mađarska vikipedija + Indonežanska vikipedija + Italijanska vikipedija + Japanska vikipedija + Korejska vikipedija + Litvanska vikipedija + Latvijska vikipedija + Majalska vikipedija + Holandska vikipedija + Novonorveška vikipedija + Norveška vikipedija + Poljska vikipedija + Portugalska vikipedija + Rumunska vikipedija + Ruska vikipedija + Slovačka vikipedija + Slovenačka vikipedija + Srpska vikipedija + Švedska vikipedija + Svahili vikipedija + Telugu vikipedija + Tai vikipedija + Turska vikipedija + Ukrajinska vikipedija + Vijetnamska vikipedija + Volapička vikipedija + Kineska vikipedija + Afrikanska vikipedija + Azerbejdžanska vikipedija + Bengalska vikipedija + Bretonska vikipedija + Bošnjačka vikipedija + Velška vikipedija + Esperanto vikipedija + Baskijska vikipedija + Irska vikipedija + Armenijska vikipedija + Islandska vikipedija + Gruzijska vikipedija + Kurdska vikipedija + Latinska vikipedija + Luksemburgška vikipedija + Makedonska vikipedija + Marati vikipedija + Navaho vikipedija + Osetska vikipedija + Sardinijska vikipedija + Srpko-hrvatska vikipedija + Albanska vikipedija + Tamilska vikipedija + Filipinska vikipedija + Uzbekistanska vikipedija + Čečenska vikipedija + Urdu vikipedija + Oksitanska vikipedija + Tatarska vikipedija + Kirgistanska vikipedija + Pandžabi vikipedija + Aragonska vikipedija + Nepalska vikipedija + Sicilijanska vikipedija + Bavarska vikipedija + Mongolska vikipedija + Napolitanska vikipedija + Blok + Ograda za stoku + Ograničavač visine + Prolaz u zidu ili ogradi + Glavni ulaz + Ulaz + Izlaz + Prelaz ulice + Vreme rada + Opis + Telefon + Veb sajt + E-pošta + Faks + Fejsbuk + Tviter + Skajp + YouTube + Instagram + VKontakte + Google+ + Mobilni + Uslužno vreme + Maksimalna visina + Maksimalna težina + Visina + Visina iznad nivoa mora + Napušteno + Napušteni objekat + Ne koristi se + Operator + Robna marka + Da + Ne + Da + Ne + Nadgledano + Nenadgledano + Da + Ne + Sušna sezona + Vlažna sezona + Proleće + Leto + Jesen + Zima + Sa stop svetlima + Nekontrolisan + Neobeležen + Početni datum + Pristup invalidskim kolicima + Da + Ne + Ograničen + Market prodaja + Privatni posed + Bez pristupa + Odredišni pristup + Pristup za korisnike + Pristup za dostavu + Pristup za poljoprivredu + Sadržaj: voda + Sadržaj: ulje + Sadržaj: gorivo + Sadržaj: vino + Sadržaj: kanalizacija + Sadržaj: gas + Sadržaj: biomasa + Sadržaj: otpadna voda + Sadržaj: pivo + Sadržaj: so + Dozvoljen + Zabranjen + Obavezan + Ograničen + Naseljenost + Podzemni + Na više nivoa + Neformalan + Širokolisno + Maslina + Jabuka + Palmino ulje + Narandže + Badem + Banana + Lešnik + Kokos + Persimon + Trešnja + Orah + Šljiva + Breskva + Čaj + Koka + Kivi + Voćnjak + Nektarina + Mango + Kaučuk + Datula + Drvo kafe + Nar + Urbano + Ruralno + Na snazi: semafor + Na snazi: maks. brzina + Na snazi: maks. visina + Na snazi: putarina + Sklonište za životinje + Psi + Mačke + Psi, mačke + Ptice + Divlje životinje + Konj + Sisari + Ribe + Sedišta + Naslon: da + Naslon: ne + Zgrada + Izvor energije: ugalj + Izvor energije: gas + Izvor energije: nuklearni + Izvor energije: solarni + Izvor energije: hidro + Izvor energije: vetar + Izvor energije: geotermalni + Izvor energije: ulje + Izvor energije: dizel + Međunarodno ime + Nacionalno ime + Regionalni naziv + Lokalni naziv + Stari naziv + Alternativni naziv + Ručno + Na struju + Bez pumpe + Da + Mineralna + Blato + Sumporna + Karta + Kancelarija + Terminal + Karta planinarenja + Priroda + Istorija + Znak + Audio vodič + Biciklistička karta + Taktilni model + Taktilna karta + Biljke + Geologija + Javni prevoz + Tehnologija + Astronomija + Da + Da + Ne + Zabranjeno + Samo napolju + Dozvoljeno + U posebnoj prostoriji + U izolovanoj sobi + Da + Da + Zvuk : ne + Samo kada je dozvoljeno hodanje + Stanica za spašavanje + Mini kružni tok + Prelaz pruge + Mesto za posmatranje ptica + Jahanje konja + Bašta + Trava + Pesak + Štandovi + Specijalna zgrada + Šupa + Mešano + Vrste + Rod + Takson + Povremeno + Tabla + Markacija na drvetu + Marker puta + Oglasna tabla + Nema ognjišta + Za pušače + Putarina + Bez putarine + Mrtvo drvo + Nivo + Laka + Srednja + Napredna + Početnička + Ekspertska + Slobodna vožnja + Klasično + Klasično+klizanje + Skuterom + Klizanje + Ne + Mogul + Tip bašte: stambena + Tip bašte: javna + Tip bašte: privatna + Tip bašte: botanička + Tip bašte: kuhinjska + Tip bašte: rozarijum + Tip bašte: francuski + Tip bašte: engleski + Tip bašte: japanski + Kapacitet + Da + Nema posebnih mesta za osobe sa invaliditetom + Posebna mesta za osobe sa invaliditetom + Posebna mesta za žene + Nema posebnih mesta za žene + Posebna mesta za žene + Posebna mesta za učenike + Posebna mesta za nastavnike + Posebna mesta za roditelje + Bez posebnih mesta za roditelje + Posebna mesta za roditelje + Kapacitet na sat + Prosečno vreme putovanja, u minutima + Balon + Bez balona + Da + Bez grejanja + Dozvoljene + Bicikle: nedozvoljene + Dozvoljene samo leti + Samo ulaz + Samo izlaz + Ulaz i izlaz + Letnji pristup: samo ulaz + Letnji pristup: samo izlaz + Letnji pristup: ulaz i izlaz + Sobe + Ljubavni hotel + Kovanice + Kovanice se ne prihvataju + $0.5 kovanice + 50c, 1€ i 2€ kovanice + Telefonske kartice + Telefonske kartice se ne prihvataju + Kreditne kartice + Kreditne kartice se ne prihvataju + Novčanice + Novčanice se ne prihvataju + Elektronske tašne + Elektronske tašne se ne prihvataju + Keš + Keš se ne prihvata + Debitne kartice + Debitne kartice se ne prihvataju + Bitkoin + Bitkoin se ne prihvata + Visa kartice + Visa kartice se ne prihvataju + MasterCard kartice + MasterCard kartice se ne prihvataju + Maestro kartice + Maestro kartice se ne prihvataju + Kapacitet kabine/klupe/kola + 1 (turistički) + 1S (turistički superior) + 2 (standard) + 2S (standard superior) + 3 (komfort) + 3S (komfort superior) + 4 (prva klasa) + 4S (prva klasa superior) + 5 (luksuz) + 5S (luksuz superior) + Kartice naloga + Kartice naloga se ne prihvataju + American Express (AMEX) kartice + American Express (AMEX) kartice se ne prihvataju + Diners Club kartice + Diners Club kartice se ne prihvataju + DKV kartice + DKV kartice se ne prihvataju + UTA kartice + UTA kartice se ne prihvataju + Efectivo kartice + Efectivo kartice se ne prihvataju + Girocard kartice se ne prihvataju + Discover Card kartice + Discover Card kartice se ne prihvataju + Visa Electron kartice + Visa Electron kartice se ne prihvataju + Lajtkoin + Lajtkoin se ne prihvata + Čekovi + Čekovi se ne prihvataju + PIKEPASS + IC Stored Fare kartice se ne prihvataju + Pripejd tiketi + JCB kartice + JCB kartice se ne prihvataju + Laser kartice + Laser kartice se ne prihvataju + Quick kartice + Quick kartice se ne prihvataju + Eurowag kartice + Eurowag kartice se ne prihvataju + E‑ZPass kartice + E‑ZPass kartice se ne prihvataju + Euroshell kartice + Euroshell kartice za gorivo se ne prihvataju + KITCard kartice + KITCard kartice se ne prihvataju + Westfalen kartice + Westfalen kartice se ne prihvataju + V PAY kartice + V PAY kartice se ne prihvataju + Dodžkoin + Dogecoin se ne prihvata + Cibus kartice + Cibus kartice se ne prihvataju + Keš kartice (Geldkarte) + Keš kartice (Geldkarte) se ne prihvataju + Proton + Proton kartice se ne prihvataju + AvantCard kartice + AvantCard kartice se ne prihvataju + MEP kartice + MEP kartice se ne prihvataju + Minipay kartice + Minipay kartice se ne prihvataju + MiniCash + MiniCash se ne prihvata + Moneo + Moneo se ne prihvata + Monedero 4B + Monedero 4B se ne prihvata + Monedero + Monedero se ne prihvata + BankAxess + BankAxess se ne prihvata + Coinkite + Coinkite se ne prihvata + Roadrunner kartice + Roadrunner kartice se ne prihvataju + SVG + SVG se ne prihvata + Plaćanje SMS-om + Plaćanje SMS-om se ne prihvata + OV-Chipkaart + OV-Chipkaart se ne prihvata + Oyster kartice + Oyster kartice se ne prihvataju + SUBE kartice + SUBE kartice se ne prihvataju + Via Verde + Via Verde se ne prihvata + Vaučeri za jelo + Vaučeri za jelo se ne prihvataju + Pejpal + Pejpal se ne prihvata + U-Key + U-Key se ne prihvata + Žetoni + Žetoni se ne prihvataju + Poklon vaučeri + Poklon vaučeri se ne prihvataju + Golden Crown kartice + Golden Crown kartice se ne prihvataju + PRO100 kartice + PRO100 kartice se ne prihvataju + Union Card kartice + Union Card kartice se ne prihvataju + MTS-Money + MTS-Money se ne prihvata + Yandex.Money + Yandex.Money se ne prihvata + Opis plaćanja + Vegeterijanska + Vegeterijanska (pomalo) + Isključivo vegeterijanska + Vegeterijanska + Vegeterijanska ishrana: ne + Veganska + Isključivo veganska + Veganska + Veganska ishrana: ne + Bezglutenska + Isključivo bezglutenska + Bezglutenska + Bezglutenska ishrana: ne + Košer + Isključivo košer + Košer + Oslonac za prepovijanje pelena + Sto za prepovijanje pelena + Nema stola za prepovijanje pelena + Soba za prepovijanje pelena + Ograničeno vreme parkiranja + Karte za parkiranje + Cigarete + Kese za izmet ljubimaca + Karte za javni prevoz + Piće + Slatkiši + Kondomi + Markice + Hrana + Piće i hrana + Gorivo + Mleko + Izduženi novčići + Hleb + Žvakaće gume + Karte za parkiranje; karte za javni prevoz + Kafe aparat + Automat za putarinu + Automat za karte + Automat za vodu + Telefonski vaučeri + Hrana za životinje + Mapa javnog prevoza + Piće i slatkiši + Vaučeri + DVD-ovi + Sveće + Igračke + Sladoled + SIM kartice + Grana + Ratna spomenica + Spomen-ploča + Statua + Kamen + Stela + Bista + Košinto + Krstolik + Vozilo + Kamen spoticanja + Obelisk + Stub + Stupa + Prasat + Fasadni ukras + Natpis + Istorijski kamenolom + Mešavina + Antimon + Azbest + Barit + Bazalt + Boksit + Beril + Bizmut + Hromit + Glina + Ugalj + Bakar + Dijamant + Dolomit + Gas + Zlato + Grafit + Šljunak + Gips + Ruda gvožđa + Olovo + Lignit + Krečnjak + Mermer + Živa + Tinjci + Mineralno ulje + Nikl + Ulje + Opal + Treset + Platina + Radijum + Rubin + Rutil + So + Pesak + Peščar + Silicijum + Srebro + Argilošist; ruda gvožđa; bakar + Argilošist + Kamen + Kalaj + Tuf + Uranijum + Vanadijum + Voda + Cink + Cirkon + Vlažna livada + Tresava + Močvara + Mangrove + Blato + Pesak + Staroverci + Spiritisti + Jermenski apostoli + Holandski reformisti + Apostolska crkva + Reformisti + Vrsta goriva (avia) + Vođice za slabovide + Bez četkica + Odredište + Apoteka (na recept) + Veroispovest + Zabit + Usluga + Hrana + Zdravstvene usluge + Izvor napajanja + Usluge + Radno stanje + Tip dostave vode + Mesto pristupa vodi + Tip kontrolnog mesta + Prodavnica + Prodavnica potrebština i samousluga + Hitna pomoć + Služba hitne pomoći + Pešački/biciklistički mrežni čvorovi + Ograničenje saobraćaja + Napravljeno ručno + Saobraćajna konstrukcija + Dostava vode + Korišćenje zemljišta + Smeštaj + Zabavno + Usluga + Radionica + Prodavnica potrebština + Prodavnica delikatesa + Prodavnica zelenila + Prostor sa sladoledom + Prodavnica peciva + Mlekara + Automat prodaje + Prodavnica torbi + Nameštaj za kupatilo + Prodavnica kreveta + Prodavnica priloga + Ronilačka oprema + Prodavnica za kućno unapređenje + Oprema za pecanje + Prodavnica stvari slobodnog letenja + Prodavnica nameštaja + Baštenski centar + Prodavnica baštenskog nameštaja + Opšta prodavnica + Staklara + Gvožđara + Prodavnica bilja + Prodavnica kućnih potrebština + Kuhinjski nameštaj + Medicinska dostava + Prodavnica spoljnje opreme + Radiotehnička prodavnica + Brodska oprema + Prodavnica kancelarijske opreme + Prodavnica pribora za jelo + Trgovački terminal + Prodavnica mešane robe + Prodavnica prozorskih zastora + Robna kuća + Energetsko odeljenje + Prodavnica auta + Prodavnica točkaša + Drakstor + Umanjeni modeli + Prodavnica zanatskih rekvizita + Religiozna dobra + Hitan telefon + Protivpožarna metla + Protivpožarni bazen + Sanduk soli + Pešački kameni most + Ispupčenje (brzinsko) + Traka neravnine + Usporivački prelaz + Potiskivač + Stop pozicija javnog prevoza + Peron pruge + Pružni prelaz + Pružni predzaustavljač + Brodogradilišna pruga + Stanica žičare + Sedišta žičare + Sedišta sa t-prečkom + Sedišta sa j-prečkom + Prevozna prečka žičare + Žičara sa gondolama i sedištima + Vučna žičara + Žičara sa vučnim konopcem + Stub žičare + Kružni kraj puta + Autoputna raskrsnica + Hidrant + Brodski suvi dok + Fabrika otpadnih voda + Vodeni prelaz + Niska brana + Sedimentna brana + Podstanica + Odeljenje kablovskog razvoda + Električni toranj + Električni stub + Telefonski toranj + Rashladni toranj + Zidarski šut + Iverica + Suvi malter + Deponija + Pretgrađevinski teren + Novograđevinski teren + Podzemni ulaz + Izvor nafte + Nadzorna tačka + Kriket mreže + Kancelarija za građane + Parcelna grupa + Prihvatilište beba + Terapeut profesija + Ortoped donjih ekstremiteta + Logoped + Telekomunikaciona kancelarija + Kancelarija biroa rada + Istraživačka kancelarija + Novinarska kancelarija + Kancelarija arhitekta + Kladionica + Kancelarija za religije + Kancelarija udruženja + Finansijska kancelarija + Osnivačka kancelarija + Kancelarija poreskog savetnika + Klizalište + Sportski teren + Skakanje + Boćanje + Kuglanje + Konjički + Kuglanje na ledu + Mali tenis + Streljaštvo + Umetnost + Vojni poligon + Granični kamen + Runski kamen + Zabavna vožnja + Točak ringišpil + Izdvojena kabina + Koliba + Otvorena koliba + Lovačka koliba + Pokloničko mesto + Unitarni univerzalizam + Više-verski + Tenrikio + Luterizam + Anglikanizam + Prezbiterijanizam + Evangelizam + Pentakostalizam + Reformizam + Novo-apostolizam + Episkopalizam + Šia islam + Unitarizam + Grčki katolicizam + Tibetanizam + Reformisan unitarizam + Crkva Hristos + Ujedinjenje + Menonitizam + Božji sabori + Nedenominacijski + Nazarećani + Unitarni metodisti + Kongregacionalizam + Sveci kasnijeg dana + Ujedinjena Crkva Hristova + Naučna religija + Etiopsko Tevahedo pravoslavlje + Unitarci + Koptsko pravoslavlje + Veslejani + Šaktizam + Šingon Šu + Maroniti + Internet pristup: VLAN + Internet pristup: terminal + Internet pristup: servis + Religiozna oblast + Krst sputa + Hram sputa + Karavan lokacija + Groblje + Tranvaj usponski + Hanami + Umetnički klub + Klub igara za stolom + Kinematografski klub + Donatorski klub + Skijaška staza + Staza za kočije + Staza za sanke + Umetnički centar + Saborni centar + Lovačka čeka + Centar sela + Konjička stanica + Samoposlužna kuhinja + Poljoprivredne mašine + Pčelar + Kovač + Štampar + Postavljač tepiha + Pripremač jela + Krojač + Staklorezač + Zlatar + Kovač + Ključar + Metalorezac + Foto-tamna soba + Moler + Keramičar + Konopčar + Konstruktor krovova + Sedlar + Jedrar + Konstruktor skela + Konstruktor štandova + Kamenorezaz + Konstruktor suncobrana + Čistač + Parketar + Kovač posuđa + Tapetar + Tehničar računara + Hemijsko čišćenje + Prosek + Zahod;Toaleti + Javna kuća + Pušački deo + Zalagaoničar + Mesto kamena + Sidrište + Sidrišna bova + Svetionik + Navigacioni svetionik + Lateralni svetionik + Svetionik bezbednih voda + Svetionik posebne namene + Dok + Nautička zgrada + Nautički most + Navigaciona bova + Dnevna oznaka + Oznaka udanjenosti + Nasip + Signal magle + Oznaka zemlje + Svetlo, nautika + Glavno svetlo, nautika + Sporedno svetlo, nautika + Plutajuće svetlo, nautika + Svetlo plovila, nautika + Usidrenje + Vodena rampa + Obaveštenje, nautički + Sidrišni ankor + Radarski transponder, nautički + Radio stanica, nautički + Signalna stanica, saobraćaj + Signalna stanica, upozorenje + Prostor za manja plovila + Oznaka na vrhu, nautički + Stena, nautički + Olupina, nautički + Prolazna visina + Prolazna visina (zatvorena) + Prolazna visina (otvorena) + Prolazna širina + Vojni aerodrom + Vojno strelište + Nevarska vikipedija + Alzašanska vikipedija + Bišnuprijska vikipedija + Farsanska vikipedija + Frisk vikipedija + Malajalamska vikipedija + Nisko Saksonska vikipedija + Pidmonteska vikipedija + Varajska vikipedija + Minangkabuska vikipedija + Kazaška vikipedija + Južno Minska vikipedija + Malagasijska vikipedija + Javanska vikipedija + Kantonska vikipedija + Astur-Leoneze vikipedija + Tadžička vikipedija + Baškirska vikipedija + Škotska vikipedija + Čuvaška vikipedija + Lombardska vikipedija + Brumanska vikipedija + Jorubska vikipedija + Gudžaratska vikipedija + Saobraćajni stub + Biciklistička barijera + Motociklička barijera + Bus traka + Obrtna vrata pune visine + Bamp prolaz + Opšta barijera + Niska ograda + Rekonstrukcijski šut + Ulični blokovi + Konjski prolaz + Kapija sa rampom + Kapija na utvrđenju + Rotaciona kapija + Obrtna vrata + Kapija za kočije + Međunarodni biciklistički mrežni čvor + Nacionalni biciklistički mrežni čvor + Regionalni biciklistički mrežni čvor + Lokalni biciklistički mrežni čvor + Međunarodni planinarski mrežni čvor + Nacionalni planinarski mrežni čvor + Regionalni planinarski mrežni čvor + Lokalni planinarski mrežni čvor + Međunarodna planinarska ruta + Nacionalna planinarska ruta + Regionalna planinarska ruta + Lokalna planinarska ruta + Referenca planinarske rute + Kolekcioni periodi + Dozvoljen pristup + Sadržaj: silosni + Sadržaj: balegna zemlja + Sadržaj: balega + Sadržaj: stočna hrana + Sadržaj: zob + Uobičajeno + Građevinska roba + Vodovodna roba + Drvena roba + Poljoprivredna roba + Roba pločica + Sidrište za bicikle + Juniciklno sidrište + Okvirno sidrište + Okasto sidrište + Igličasto + Bezlisno + Na snazi: međurastojanje + Na snazi: kontrola + Na snazi: pravo pristupa + Slepa ulica + Javna česma + Putokaz + HGV putarina + Bez površne obloge + Uslužni prostor + Pružni prelaz u nivou + Zajednički prostor odmora + Vres + Travnata zemlja + Žbun + Zemlja farme + Seča drveta + Opalo voće + Prolazno mesto + RTSA skala + Ruralni deo + Girokartica + PIKEPASS se ne prihvata + PIKEPASS (namenjeno) + Viza debitne kartice + Viza debitne kartice se ne prihvataju + Kriptovalute se ne prihvataju + Drugi metodi + Drugi metodi plaćanja se ne prihvataju + Routex da + Routex krtice goriva se ne prihvataju + Bancomat da + Bancomat se ne prihvata + IC Stored Fare - da + Vajer transferi + Vajer transferi se ne prihvataju + Mesta događaja + Košer ishrana: ne + Halal + Halal samo + Halal + Halal ishrana: ne + Bez laktoze + Bez laktoze samo + Bez laktoze + Bez laktoze ishrana: ne + Peskoterijan + Da + Ulazak kolima: ne + Da + Prolazak kroz: ne + Ime pivare + Da + Ne mikropivara + Da + Za poneti + Ne za poneti + Samo za poneti + Da + Dostava + Bez dostave + Samo dostava + Da + Sedenje napolju: da + Bez sedenja napolju + Sedenje napolju: tarasa + Sedenje napolju: trotoar + Sedenje napolju: pešačka zona + Sedenje napolju: bašta + Sedenje napolju: interno dvorište + Da + Sekond hend roba + Bez sekond hend robe + Samo sekond hend roba + Delovi + Diler + Popravka + Bez popravke + Popravka električnih vozila + Popravka motora + Da + Ne + Da + Ne automatski + Puna usluga + Da + Bez četkica: ne + Ne + Javno kupatilo + Muškarci + Zabranjeno za muškarce + Žene + Zabranjeno za žene + Unutar + Spolja + Da + Bez toaleta + Pristup toaletu za invalidska kolica: da + Pristup toaletu za invalidska kolica: ne + Pristup toaletu: kupci + Pristup toaletu: dopušten + Pristup toaletu: zajednički + Pristup toaletu: javni + Preuzimanje paketa i dopremanje pošte + Bicikl ventili + Preuzimanje paketa + Poštanska dostava paketa + Plavi memorijal + Džizo + Dimenzioni kamen + Pribor za prvu pomoć + Javne vage + Auto zaustavno mesto + Otvorena nešumovita močvara + Zatvorena nešumovita močvara + Solana + Plimski deo + Plimska močvara + Prugasta močvara + Močvarna poljana + Palsa močvara + Kameniti nasip + Kamenita + Dimnjak + Palma + Čempres + Stub zastave + Da + Bez klupe + Sa korpom + Bez korpe + Javni prevoz + Piknik + Vreme + Nadstrešnica + Prosta koliba + Zgrada + Posmatračka koliba + Kamenito sklonište + Sklonište od sunca + Poljsko sklonište + Tip skloništa: šator + Tip skloništa: napušteno + Vaj-Faj + Terminal + Kablovski + Tip pristupa internetu: javni + Tip pristupa internetu: uslužni + Bez pristupa internetu + Sa pristupom internetu + Maksimalna širina + IATA kod + ICAO kod + FAA kod + Tip dela: skulptura + Tip dela: statua + Tip dela: mural + Tip dela: arihitektura + Tip dela: slika + Tip dela: kameno + Tip dela: bista + Tip dela: instalacia + Tip dela: mozaik + Tip dela: reljef + Tip dela: grafiti + Tip dela: spomenik + Tip dela: fontana + Širina + Arhitekta + Arhitektura: moderna + Arhitektura: staljinistički neoklasicizam + Arhitektura: eklektična + Arhitektura: nova objektivnost + Arhitektura: savremena + Arhitektura: konstruktivizam + Arhitektura: okolna + Arhitektura: okvirna drvetom + Arhitektura: nova umetnost + Arhitektura: neoklasicizam + Arhitektura: viktorijanska + Arhitektura: brutalizam + Arhitektura: klasicizam + Arhitektura: gotska + Arhitektura: staroruska + Arhitektura: barokna + Arhitektura: postkonstruktivizam + Arhitektura: vilhelminski stil + Arhitektura: renesansna + Arhitektura: romanska + Vodeni tunel + Tunel putnički + Pešački tunel + Tunel železnički + Most putnički + Most za pešake + Most biciklistički + Most železnički + Most vodenog puta + Struktura mosta: pravi + Struktura mosta: prosta suspenzija + Struktura mosta: suspenzija + Struktura mosta: lučni + Struktura mosta: vezana + Struktura mosta: plutajuća + Struktura mosta: grbava + Struktura mosta: kablo-stojeći + Struktura mosta: šetačka + Struktura mosta: prosto drvena + Struktura mosta: vijadukt + Struktura mosta: akvadukt + Struktura mosta: brvnasta + Tip mosta: pokretan + Tip pokretnog mosta: baskul + Tip pokretnog mosta: okretni + Tip pokretnog mosta: vertikalni + Tip pokretnog mosta: lančani + Tip pokretnog mosta: potopljujući + Tip pokretnog mosta: transporter + Tip pokretnog mosta: uvučeći + Osvetljena: da + Osvetljena: ne + Površina: nepopločana + Površina: popločana + Površina: asfalt + Površina: betonska + Površina: razređena kaldrma + Površina: okrugla kaldrma + Površina: obrađena kocka + Površina: okrugla rizla + Površina: kamena + Površina: metalna + Površina: drvena + Površina: rizlana + Površina: fino rizlana + Površina: kompaktovana + Površina: pesak + Površina: trava + Površina: šuplja kaldrma + Površina: opšta + Površina: zemlja + Površina: blato + Površina: led + Površina: so + Površina: sneg + Autobus + Deljeni taksi + Trolejbus + Tramvaj + Voz + Laka pruga + Monopruga + Uspenska železnica + Trajekt + Metro + Ruta pruge + Istorijski objekat + Nadzor: da + Sa prodavnicom + Maloprodaja + Maloprodaja bicikla: ne + Popravka + Popravka bicikla: ne + Iznajmljivanje + Iznajmljivanje bicikla: ne + Pumpa + Bicikl pumpa: ne + Alati za samopopravku + Alati za samopopravku bicikla: ne + Čišćenje + Čišćenje bicikla: ne + Alat lanca + Alat lanca za bicikl: ne + Maloprodaja polovnih bicikla + Maloprodaja polovnih bicikla: ne + Naplata: da + Naplata: ne + Stanica za popravku bicikala;Stanica za samo-popravku bicikala + Mesto pogleda divljeg sveta + Trening: jezik + Trening: muzika + Trening: igranje + Trening: sport + Trening: umetnost + Trening: kuvanje + Trening: računar + Trening: joga + Trening: borilačke veštine + Trening: avijacija + Trening: šišanje + Monumentalni objekat + Tip: naftna industrija + Tip: mesto izvora + Tip: fabrika + Tip: industrija gasa + Tip: drvnoprerađivačka + Tip: rafinerija + Tip: skladište + Tip: auto otpad + Tip: poljoprivreda + Tip: pivara + Tip: ciglana + Tip: međumodalni teretni terminal + Tip: pilana + Tip: prečišćenje vode + Tip: luka + Tip: depo + Beleška + Voda za životinje + Hotel za životinje + Tip hotela za životinje: pas + Tip hotela za životinje: mačka + Tip hotela za životinje: pas, mačka + Tip hotela za životinje: konj + Istorijski avion + Sa liftom + Bez lifta + Tehnički spomenik + Kamp kancelarija + Model aerodroma + Kancelarija vodiča + KANVO + Savetodavna kancelarija + Kooperativna kancelarija + Šumarska kancelarija + Kancelarija logistike + Parohijska kancelarija + Izdavačka kancelarija + 91UL gorivo + 100LL gorivo + Autogas + Avionsko A-1 gorivo + Aditiv dizel gasova + Gorivo: drvo + Gorivo: ćumur + Gorivo: ugalj + Ulična lampa + Zaustavna traka + Nadzor: unutrašnji + Nadzor: spoljašnji + Nadzor: javni + Žene + Donji veš + Muškarci + Venčanice + Sportovi + Bebe + Teksas + Radna odela + Šeširi + Krzno + Koža + Kostimi + Tradicionalno + Odela + Porodiljska + Staromodna + Veći brojevi + Školska + Plivačka + Čarape + Košulje + Plesačka + Vojna + Istorijska prekretnica + Dečja + Ženska + Sportska + Muška + Ortopedska + Istorijski posed + Tip dvorca: izražajan + Tip dvorca: odbranbeni + Tip dvorca: tvrđava + Tip dvorca: palata + Tip dvorca: kremlj + Tip dvorca: odbranbeni, izražajan + Tip dvorca: utvrđenje + Tip dvorca: širo + Tajlandska + Seksualna + Kineska + Terapeutska + Usluge masaže + Usluge saune + Usluge solarijuma + Dopušteno + Šatori nisu dozvoljeni + Otvorena vatra dozvoljena + Otvorena vatra nedozvoljena + Da + Ne + Da + Izviđački kamp: ne + Grupno samo: da + Grupno samo: ne + Da + Veš mašina: ne + Da + Karavani: ne + Improvizovano: da + Improvizovano: ne + Sanitarna otpadna stanica: da + Sanitarna otpadna stanica: ne + Izvor napajanja: ne + Izvor napajanja (utičnica): CEE 17 plava + Izvor napajanja (utičnica): CEE 7/4 + Izvor napajanja (utičnica): CEE 7/5 + Izvor napajanja (utičnica): NEMA 5-15 + Maksimalni broj šatora + Maksimalni broj karavana + Dozvoljeno za pse + Nije dozvoljeno za pse + Objekti za izbeglice + Izbeglice: ne + Grupno mesto + Usluga zbrinjavanja + Nerezidentno zbrinjavanje + Javna kuhinja + Sklonište + Ambulantna pomoć + Radionica + Dnevna nega + Starije osobe + Deca + Siročad + Osobe sa invaliditetom + Mentalno bolesni pacijenti + Maloletnici + Beskućnici + Migranti + Maloprivilegovani + Narkozavisnici + Nezaposleni + Bolesni + Mlađi maloletnici + Filharmonija + Muzikal + Drama + Opera + Kabare + Lutkarstvo + Komedija + Kamerna muzika + Marionetski + Balet + Predstava senki + Poligonalna bina + Zatvoreni lonac + Proizvod: cigla + Proizvod: negašen kreč + Proizvod: koža + Proizvod: kreč + Proizvod: pivo + Proizvod: ćumur + Proizvod: meso + Opis za korisnike invalidskih kolica + Opis za slepe + Opis za gluve + Stepenice + Rukohvat: da + Rukohvat: ne + Rampa: da + Rampa: ne + Rampa za invalidska kolica: da + Rampa za invalidska kolica: ne + Rampa (sa stepenicama): da + Rampa (sa stepenicama): ne + Rampa za bicikle: da + Rampa za bicikle: ne + Rampa za prtljag: da + Rampa za prtljag: ne + Broj stepenika + Stanje stepenika: isto + Stanje stepenika: različito + Stanje stepenika: grubo + Gomila kamenja + Defibrilator + Defibrilator: da + Tip: ratno groblje + Tip: humka + Tip: u steni + Tip: podzemna + Tip: komora + Tip: kolumbarijum + Tip: mauzolej + Tip: sarkofag + Tip: kripta + Tip: piramida + Civilizacija: starorimska (753.g. pre.H - 284.g. posl.H) + Civilizacija: nuragska (18. v.pre.H - 2. v.posl.H) + Civilizacija: etrurska (12. v. - 6. v.posl.H) + Civilizacija: praistorijska + Civilizacija: starogrčka + Civilizacija: rimska + Civilizacija: vizantijska (285.g. posl.H - 1453.g. posl.H) + Civilizacija: imperijalnokineska (221.g. pre.H - 1911.g. posl.H) + Civilizacija: staroegipadska (do 332.g. pre.H) + Civilizacija: keltska + Civilizacija: zapadnorimska (285.g. posl.H - 476.g. posl.H) + Civilizacija: mikenska + Civilizacija: dačanska + Civilizacija: hernička + Civilizacija: grčko egipadska (332.g. pre.H - 30.g. pre.H) + Civilizacija: rimsko i vizantijsko egipadska + Civilizacija: eladska + Civilizacija: cikladska + Civilizacija: minonska + Civilizacija: tiahunakotska + Trening životinja + Pas + Konj + Dozvoljeno konjima + Nedozvoljeno konjima + Dozvoljeno životinjama + Nedozvoljeno životinjama + Istorijski period: nuragski + Istorijski period: kameno doba / bronzano doba (neodređeno) + Istorijski period: bronzano doba + Istorijski period: principat (27.g. pre.H - 284.g. posl.H) + Istorijski period: klasično grčki (5.v. - 4.v. pre.H) + Istorijski period: arhaično grčki + Istorijski period: gvozdeno doba + Istorijski period: Rimska republika (508.g. pre.H - 27.g. pre.H) + Istorijski period: helensko grčki + Istorijski period: novo carstvo (1550 - 1069 pre.H) + Istorijski period: srednje carstvo (2055 - 1650 pre.H) + Istorijski period: predinastijsko egipadski + Istorijski period: rano dinastijski period (3100 - 2686 pre.H) + Istorijski period: staro carstvo (2686 - 2181 pre.H) + Istorijski period: prvi međuperiod (2181 - 2055 pre.H) + Istorijski period: drugi međuperiod (1650 - 1550 pre.H) + Istorijski period: treći međuperiod (1069 - 664 pre.H) + Istorijski period: prvi persijski period + Istorijski period: kasni period (664 - 332 pre.H) + Istorijski period: drugi persijski period + Istorijski period: Aleksandra Velikog + Istorijski period: ptolomejsko egipadski (305 pre.H - 30 pre.H) + Istorijski period: hrišćansko egipadski + Istorijski period: vizantijsko egipadski (30 pre.H - 641 posl.H) + Istorijski period: persijska okupacija + Istorijski period: Grčki Srednji Vek (1100 - 800 pre.H) + Istorijski period: Rimsko Grčki (146 pre.H - 330 posl.H) + Istorijski period: Rimsko carstvo (753.g. pre.H - 509.g. pre.H) + Istorijski period: dominirajući (285.g. posl.H - 476.g. posl.H) + Istorijski period: seljački (epoha 1., 2., 1580 pre.H - 133 posl.H) + Istorijski period: urbanski (epoha 3., 133 posl.H - 374 posl.H) + Istorijski period: klasični (epoha 4., 374 posl.H - 900 posl.H) + Istorijski period: iperijalni (epoha 5., 900 posl.H - 1200 posl.H) + Istorijska epoha: paleolitska (2,6 miliona god. - 10000 BP) + Istorijska epoha: mezolitska + Istorijska epoha: neolitska + Istorijska epoha: bakarno doba (4. - 3. milenijum pre.H) + Istorijski kamen + Tip: krst pomirenja + Tip: grb + ATM + Materijal: drvo + Materijal: metal + Materijal: ojačani beton + Materijal: beton + Materijal: čelik + Materijal: kamen + Materijal: zidarski + Materijal: cigla + Materijal: plastika + Materijal: kameni pesak + Materijal: granitni kamen + Materijal: metal, drvo + Materijal: staklo + Materijal: bronza + Materijal: zemlja + Materijal: kompozit + Materijal: krečnjak + Materijal: mermer + Materijal: aluminijum + Megalit + Humka + Utvrđenje + Naselje + Velegrad + Krug kućišta + Petroglif + Zemljana barijera + Vojni aerodrom + Vojni/civilni + Teretana na otvorenom + Biohemija + Teretana + Fitnes + Da + Tip kuhinje + Pice + Pljeskavice + Kafa + Sendviči + Kebab + Doner kebab (šavurma) + Piletina + Sladoled + Suši + Plodovi mora + Roštilj + Nudle + Krofne + Ramen + Palačinke + Doručak + Šnicle + Tapas + Kari + Gril + Večera + Kobasice + Torte + Američke palačinke + Testenine + Brza hrana + Sokovi + Čaj + Prodavnica čaja + Supe + Pite + Hotdog + Salate + Pržena hrana + Bistro + Pekara + Kuskus + Pržena piletina + Dezerti + Takosi + Falafel + Smuti + Suvlaki + Jogurt + Giros + Palačinke + Jakiniku + Suki + Udon + Jakotori + Meso + Krilca + Vafle + Čokolada + Vino + Krompir + Branč + Pita + Fandu + Bageti + Buritosi + Terijaki + Šavurma + Regionalna + Italijanska + Kineska + Meksička + Japanska + Nemačka + Indijska + Američka + Azijska + Francuska + Grčka + Tajlandska + Internacionalna + Turska + Španska + Vijetnamska + Korejska + Mediteranska + Bavarska + Libanska + Ruska + Filipinska + Portugalska + Gruzijska + Poljska + Brazilska + Arapska + Danska + Indonežanska + Afrička + Karipska + Argentinska + Balkanska + Peruanska + Hrvatska + Bolivijska + Malagaška + Persijska + Marokanska + Austrijska + Malezijska + Irska + Etiopijska + Mađarska + Laoska + Evropska + Uzbekistanska + Češka + Kubanska + Britanska + Latinoamerička + Nepalska + Mongolska + Bliskoistočna + Ukrajinska + Avganistanska + Belgijska + Baskijska + Švajcarska + Švedska + Jamajčanska + Jermenska + Havajska + Engleska + Pakistanska + Tajvanska + Teks-meks + Holandska + Sirijska + Australijanska + Kadžun + Egipatska + Senegalska + Jevrejska + Bugarska + Tibetanska + Barometar + Barometar: ne + Roštilj: da + Prodavnica na veliko + Knjige + Park i vožnja + Da + Pristup vozilima za sneg + Komunalni + Porodični + Za više porodica + Tip + Pristup autobusu + Pristup karavanu + Pristup prikolici + Pristup motociklima + Pristup mopedu + HGV pristup + Pristup vozilima za laku robu + Pristup za osobe sa invaliditetom + Pristup vozilom: da + Pristup vozilom: privatni + Pristup vozilom: ne + Pristup vozilom: samo do odredišta + Pristup vozilom: odobren + Pristup vozilom: za mušterije + Pristup vozilom: vojni + Pristup vozilom: dostava + Pristup vozilom: šumski saobraćaj + Pristup motornim autom: da + Pristup motornim autom: privatni + Pristup motornim autom: ne + Pristup motornim autom: samo do odredišta + Pristup motornim autom: odobren + Pristup motornim autom: za mušterije + Pristup motornim autom: šumski saobraćaj + Pristup motornim vozilom: da + Pristup motornim vozilom: privatni + Pristup motornim vozilom: ne + Pristup motornim vozilom: samo do odredišta + Pristup motornim vozilom: odobren + Pristup motornim vozilom: za mušterije + Pristup motornim vozilom: vojni + Pristup motornim vozilom: dostava + Pristup motornim vozilom: šumski saobraćaj + Pristup motornim vozilom: poljoprivredni + HGV pristup: privatni + HGV pristup: ne + HGV pristup: poljoprivredni + HGV pristup: nepreporučen + HGV pristup: nepogodno + Pristup za laka dostavna vozila: ne + Pristup za motocikle: privatni + Pristup za motocikle: ne + Pristup za mopede: ne + Pristup za bicikle: privatni + Pristup za bicikle: samo uz guranje bicikle + Pristup za bicikle: samo do odredišta + Pristup za bicikle: odobren + Pristup za bicikle: za mušterije + Pristup konjem: privatni + Pristup konjem: samo do odredišta + Pristup konjem: odobren + Pristup konjem: šumski + Pristup za pešake: da + Pristup za pešake: privatni + Pristup za pešake: ne + Pristup za pešake: samo do odredišta + Pristup za pešake: odobren + Pristup za pešake: za mušterije + Pristup za karavan: ne + Pristup za prikolice: ne + Pristup za autobuse: ne + Pristup za turističke autobuse: da + Pristup za turističke autobuse: namenjen + Pristup za turističke autobuse: ne + Ski pristup: da + Ski pristup: ne + Pristup za snoumobil: privatni + Pristup za snoumobil: ne + Pristup za poljoprivredna vozila: da + Pristup za poljoprivredna vozila: ne + Pristup za taksi: da + Pristup za taksi: namenjen + Pristup za taksi: ne + Pristup za invalide: ne + Tip megalita: menhir + Tip megalita: dolmen + Tip megalita: nuragi + Tip megalita: kameni krugovi + Tip megalita: koridorna grobnica + Tip utvrđenja: gradina + Tip utvrđenja: limes + Tip utvrđenja: šanac + Tip utvrđenja: ukopani kružni šanac + Pa (maorska utvrđena naseobina) + Istorijsko imanje + Istorijska železnička stanica + Istorijsko gumno + Istorijska vešala + Istorijske šine + Trg + Umetnik + Vajar + Tip zgrade: crkva + Tip zgrade: kapela + Tip zgrade: džamija + Tip zgrade: hram + Tip zgrade: katedrala + Tip zgrade: manastir + Tip zgrade: bazilika + Tip zgrade: sinagoga + Tip zgrade: svetilište + Krst + Krst na vrhu: da + Bivši zatvorenički logor + Tip: koncentracioni logor + Tip: zarobljenički logor + Tip: radni logor + Tip: nacistički + Tip: gulag + Temperatura + Kupanje: da + Kupanje: ne + Skladište za brodove + Pokretno: da + Putnici + Vozila + Bicikli + Kontejneri + HGV + Hakerski prostor + Servis za elektroniku + Prodavnica vatrometa + Dužina + Opšta praksa + Oftamologija + Ginekologija + Interno + Ortopedija + Otorinolaringologija + Pedijatrija + Medicinska specijalizacija: pedijatrija: ne + Dermatologija + Urologija + Opšta hirurgija + Radiologija + Psihijatrija + Neurologija + Kardiologija + Traumatologija + Klinička biologija + Osteopatija + Anesteziologija + Pulmologija + Zubna, oralna i maksilofacijalna hirurgija + Porodiljsko + Urgentna medicina + Medicinska specijalizacija: urgentna medicina: ne + Plastična hirurgija + Fizioterapija + Okupaciona terapija + Dečija psihijatrija + Fizikalna medicina i rehabilitacija + Maksilofacijalna hirurgija + Medicinsko snimanje + Gastroenterologija + Stomatologija + Nefrologija (renalne bolesti) + Neurohirurgija + Neuropsihijatrija + Endokrinologija + Nuklearna medicija + Patologija + Onkologija + Tropska medicina + Medicinska specijalizacija: tropska medicina: ne + Akušerstvo (postnatalno) + Medicinska specijalizacija: akušerstvo (postnatalno): ne + Akušerstvo (prenatalno) + Medicinska specijalizacija: akušerstvo (prenatalno): ne + Društvena pedijatrija + Medicinska specijalizacija: društvena pedijatrija: ne + Akušerstvo (carski rez) + Medicinska specijalizacija: akušerstvo (carski rez): ne + Lečenje bolesti zavisnosti + Optometrija + Klinička patologija + Logoped + Sportska masaža + Sportska medicina + Akupunktura + Homeopatija + Tradicionalna kineska medicina + Reiki + Travarstvo + Kiropraktičar + Naturopatija + Dubinska psihologija + Bihejvioralna terapija + Palijativna medicina + Tip zgrade: piramida + Bilijar + Mikrotalasna: da + Mikrotalasna: ne + Grejač za vodu: da + Grejač za vodu: ne + Tip: podloga + Garaže + Natkrivena + Dodžo + Galske igre + Trčanje + Karting + Badmington + Džudo + Futsal + Trkanje kolima na daljinsko upravljanje + Skvoš + Lakros + Boks + Zvanično ime + Dajk + Delfin + Denotacija + Kategorija težine + Radioterapija + Reddit + Istorijski rezervoar + Izvor energije: biomasa + Podizanje novca + Tip + Na otvorenom + Poljoprivredna radnja + Tip: poljoprivredno + Pritisak + Video + SMS + Status + Tip + Tip eksplozije: kraterska rasprskavanja (tik ispod površine) + Kratko ime (engl.) + Niz eksplozija + Vreme eksplozije (UTC) + Tip eksplozije: podvodna + Tip eksplozije: svemir (preko 80km nadmorske visine) + Tip eksplozije: u atmosferi, raketa ili projektil + Tip eksplozije: podzemna + Tip eksplozije: u atmosferi, vodena podloga, barža + Tip eksplozije: u atmosferi, podloga + Tip eksplozije: u atmosferi, balon + Tip eksplozije: u atmosferi, podloga, toranj + Tip eksplozije: u atmosferi, kapljice + Tip eksplozije: u atmosferi + Tip eksplozije: podzemna, tunel + Tip eksplozije: podzemna, jama + Eksplozija: mesto + Eksplozija: država + Objekat pod zaštitom: voda + Objekat pod zaštitom: naselje + Objekat pod zaštitom: priroda + Objekat pod zaštitom: istorijski + Naslov zaštite + Zaštićena oblast + Mesto za vatru + U izgradnji + Ref. + Ref. tunela + Ref. mosta + Akademske + Dečije + Religijske + Antikvarnica + Strip + Sveska + Tip: polica + Jačina udarnog talasa + Nulti nivo nadmorske visine + Visina vatrene lopte + Krater eksplozije + Snaga eksplozije + Vreme eksplozije (UTC) + Svrha eksplozije: industrijska primena, pomeranje zemlje + Svrha eksplozije: industrijska primjena + Svrha eksplozije: industrijska primena, stimulacija nafte + Svrha eksplozije: industrijska primena, seizmičko ozvučavanje + Svrha eksplozije: industrijska primena, iskopavanje šupljina + Svrha eksplozije: fundamentalna nauka + Svrha eksplozije: istraživanje za mirnodopske primene + Svrha eksplozije: bezbednosni eksperiment + Svrha eksplozije: efekti oružja + Svrha eksplozije: povezano sa nuklearnim oružijem + Bendi + Samo proizvodi sajamske trgovine + Sajamska trgovina: ne + Sajamska trgovina: da + Ulični orman + ATM: ne + ATM: da + Natpis:NE + Natpis:E + Natpis:SE + Natpis:S + Natpis:SW + Natpis:W + Natpis:NW + Natpis:N + Pokazivač: šumarsko odeljenje + Pokazivač: šumarsko odeljenje + Disk golf + Psihijatrija za odrasle + Podologija + Ortodonija + Dermato-venereologija + Strelica + Vibracija + Mofa pristup + Pristup motociklima + Stena penjanja + Savetovanje (bračno): ne + Savetovanje (bračno): da + Savetovanje (imigrant): ne + Savetovanje (imigrant): da + Savetovanje (beskućno): ne + Savetovanje (beskućno): da + Savetovanje (porodično): ne + Savetovanje (porodično): da + Savetovanje (obrazovanje): ne + Savetovanje (obrazovanje): da + Savetovanje (oko droga): ne + Savetovanje (oko droga): da + Savetovanje (krizno): ne + Savetovanje (krizno): da + Savetovanje (za parove): ne + Savetovanje (za parove): da + Savetovanje (smernice za dete): ne + Savetovanje (smernice za dete): da + Savetovanje (prenatalno): ne + Savetovanje (prenatalno): da + Savetovanje (zavisnost): ne + Savetovanje (zavisnost): da + Uloga zdravstvenog radnika: veštak + Uloga zdravstvenog radnika: tehničar + Uloga zdravstvenog radnika: pomoćnik lekara + Uloga zdravstvenog radnika: terapeut + Uloga zdravstvenog radnika: psiholog + Uloga zdravstvenog radnika: podolog + Uloga zdravstvenog radnika: lekar + Uloga zdravstvenog radnika: bolničar + Uloga zdravstvenog radnika: medicinska sestra + Uloga zdravstvenog radnika: babica + Uloga zdravstvenog radnika: iscelitelj + Uloga zdravstvenog radnika: asistent + Zid + Pond + Suvo bure + Stub + Podzemni + Tačka usisavanja + Tip zdravstvene ustanove: dom grupe za podršku + Tip zdravstvene ustanove: starački dom + Tip zdravstvene ustanove: prva pomoć + Tip zdravstvene ustanove: ambulanta + Tip zdravstvene ustanove: odeljenje + Tip zdravstvene ustanove: terapija + Tip zdravstvene ustanove: laboratorija + Tip zdravstvene ustanove: savetovalište + Tip zdravstvene ustanove: terenska bolnica + Medicinska kancelarija + Zdravstvena usluga: test: ne + Zdravstvena služba: test: da + Zdravstvena služba: podrška: ne + Zdravstvena služba: podrška: da + Zdravstvena služba: vakcinacija: ne + Zdravstvena služba: vakcinacija: da + Zdravstvena služba: prevencija: ne + Zdravstvena služba: prevencija: da + Zdravstvena usluga: dečje odeljenje: ne + Zdravstvena usluga: dečje odeljenje: da + Zdravstvena služba: pregled: ne + Zdravstvena služba: pregled: da + Zdravstvena služba: savetovanje: ne + Zdravstvena služba: savetovanje: da + Zdravstvena služba: porodilište: ne + Zdravstvena služba: porodilište: da + Unani + Sida + Tradicionalno Tibetanski + Tradicionalno mongolski + Kampo + Ajurveda + Tradicionalno Kineski + Zapadni + Sanitarna otpadna stanica + Solarijum + Smernice za goste: ne + Smernice za goste: da + Orijentacija mesta slobodnog letenja: Z + Orijentacija mesta slobodnog letenja: JZ + Nema vreme leta (slobodno letenje) + Orijentacija mesta slobodnog letenja: SZ + Orijentacija mesta slobodnog letenja: J + Orijentacija mesta slobodnog letenja: JI + Orijentacija mesta slobodnog letenja: I + Orijentacija mesta slobodnog letenja: SI + Orijentacija mesta slobodnog letenja: S + Slobodno letenje kruto: ne + Kruta + Zmajarstvo: ne + Zmajarstvo + Paraglajding: ne + Paraglajding + Zvanično: ne + Zvanično: da + Obuka + Vuča + Sletanje na vrhu + Sletanje + Poletanje + Upravljano dugmetom: ne + Upravljano dugmetom: da + Slavina za vodu + Slobodno letenje (sport) + Usisivač + Usisavač: ne + Da + Komprimovani vazduh: ne + Da + Rasadnik biljaka + Ime kuće + Eksplozija: uređaj + Eksplozija: bojeva glava + Eksplozija: prečnik kratera + Plotun eksplozija: druga ili kasnija detonacija plotun testa + Plotun eksplozija: prva detonacija plotun testa + Kuglanje + Mreža + Prodaja: polovna + Prodaja: da, polovna + Prodaja: ne + Prodaja + Štandovi + Reptili + Sokolarnik + Ptičnik + Ptice + Safari park + Kućište + Park divljih životinja + Zoološki vrt sa maženjem životinja + Životni prsten + Škola stranih jezika + Muzička škola + Dečiji kamp + Popravka elektronike: TV + Popravka elektronike: telefon + Popravka elektronike: aparati + Popravka elektronike: računari + Kancelarija snabdevača energije + Vino: servirano + Vino: maloprodaja + Vino: da + Da + Zajednički radni prostor + Teretna stanica + Konstrukcija: skrivena + Konstrukcija: kupola + Konstrukcija: tanjir + Konstrukcija: samostojeća + Konstrukcija: rešetka + Tip: otvorena štala + Bušenšank + Zatvoreno + Otvoreno + Vidljivost: područje + Vidljivost: ulica + Vidljivost: kuća + Lokacija: ulaz + Lokacija: zid + Lokacija: most + Lokacija: platforma + Lokacija: unutar + Lokacija: napolju + Lokacija: vrh krova + Lokacija: krov + Lokacija: iznad glave + Lokacija: nadzemlje + Lokacija: podvodna + Lokacija: podzemna + Metalna mreža + Dekotrava + Veštačka trava + Tartan + Glina + Planinsko područje + Kuluar + Klisura + VHF kanal + Zakonodavna institucija + Transportna institucija + Blagajna + Socijalne usluge + Socijalno osiguranje + Javni servis + Ministarstvo + Arhiva + Mreža + Kanu: ne + Kanu: da + Kajaci: ne + Kajaci: da + Gumenjak: ne + Gumenjak: da + Jedrenjaci: ne + Jedrenjaci: da + Džetski: ne + Džetski: da + Brodovi na veslanje: ne + Brodovi na veslanje: da + Kućni brodovi: ne + Kućni brodovi: da + Motorni brodovi: ne + Motorni brodovi: da + Tip: ograđen deo + Čuvanje životinja: ovca + Čuvanje životinja: konj + Čuvanje životinja + Litica + Foto studio + Dodaci ishrani + Lokomotiva + Prodavnica elektronskih cigareta + Bingo + Klađenje + Slot mašine + Pacinko + Lutrija + Vrsta + Mesto kockanja + Loto srećke + Oprema za rasvetu + Bravarska prodavnica + Elektro prodavnica + Namirnice za zabavu + Mesto za ishranu životinja + Kantonska + Pastel + Sub + Sagardotegia + Čaj sa mehurićima + Braseri + Empanada + Predjelo + Piadina + Kantina + Slane palačinke + Zamrznuti jogurt + Delikatesi + Fina jela + Soba + Bagel + Heuriger + Gastropub + Giudon + Riba i pomfrit + Tip: metalni orman + Tip: drveni ormar + Tip: kutija za čitanje + Tip: telefonska kutija + Javna polica za knjige + Božić: vebsajt + Božić: lokacija + Božić: radno vreme + Božić: beleška + Božić: period događaja + Jelka + Prodavnica drveća + Božićna radnja + Božićna piramida + Božićno tržište + Božićni događaj + Božić + Vikipedija + Dužina + Odlaganje toaleta: kantom + Odlaganje toaleta: hemijsko + Odlaganje toaleta: pitlatrine + Odlaganje toaleta: ispiranje + Rezervoar za vodu + Protivpožarni operator + Visoka komisija + Rezidencija ambasadora + Delegacija + Stalna misija + Počasni konzulat + Generalni konzulat + Konzulat + Saobraćajno ogledalo + Jedino + Ne + Da + Minimalna starost + Glavni distribucioni okvir + Akvakultura: dagnje + Akvakultura: riba + Akvakultura: škampi + Akvakultura + Prikaz informacija putnicima: ne + Prikaz informacija putnicima: da + Podrška: kula + Podrška: krov + Podrška: noseće + Podrška: plafon + Podrška: bilbord + Podrška: zemlja + Podrška: postolje + Podrška: drvo + Podrška: zid + Podrška: stub + Prikaz datuma: ne + Prikaz datuma + Higrometar: ne + Kapacitet (kreveta) + HigrometarHygrometer + Termometar: ne + Termometar + Sunčani sat + Digitalni ekran + Analogni ekran + Pumpna stanica + Ekran: ne + Ekran: da + Izlaz: biogas + Izlazna snaga biogasa + Izlaz: vakuum + Izlaz: komprimovani vazduh + Izlaz: hladna voda + Izlaz: vruć vazduh + Izlaz: para + Izlaz: topla voda + Izlaz (struja): ne + Izlaz: struja + Napon + Staklenička hortikultura + Gravitaciona + Meteorološka + Upotreba: špijunaža + Upotreba: istraživanje + Upotreba: špijunaža + Upotreba: obrazovanje + Spektar + Prečnik + Gama + Radio + Optički + Teleskop + Kartica \"Trojka\" se ne prihvata + Trojka + Status pumpe: zaključana + Status pumpe: polomljena + Status pumpe: u redu + Stil pumpe: istorijski + Stil pumpe: moderan + Tip pumpe: gravitaciona + Tip pumpe: Indija Mk II ili III> + Tip pumpe: pumpa snopa + Ventilacioni otvor + Balka + Privatni + Javni + Regionalni + Međunarodni + Isporuka: ne + Da + Nadzor prelaza: kamera + Nadzor prelaza: polaznik + Nadzor prelaza: ne + Nadzor prelaza + Prelazni saltir: ne + Prelazni saltir + Prelaz na zahtev: ne + Prelaz na zahtev + Prelazno svetlo: ne + Prelazno svetlo + Prelazno zvono: ne + Prelazno zvono + Prelazna barijera: duplo polovična + Prelazna barijera: polovična + Prelazna barijera: puna + Prelazna barijera + Prelazna barijera: ne + Aktiviranje prelaza: daljinsko + Aktiviranje prelaza: lokalno + Aktiviranje prelaza: automatsko + Rekreacioni centar + Letnji kamp + Pokvarena gomila + Određeno + So: ne + So + Dubina + Ulično postolje + Bicikl drvo + Ormarići + Vojni kontrolni punkt + Tip kabineta: ulična rasveta + Tip kabineta: vodovodni + Tip kabineta: otpadi + Tip kabineta: poštanski + Tip kabineta: gasni + Tip kabineta: kablovska televizija + Tip kabineta: telekomski + Tip kabineta: energetski + U upotrebi: da + Tok + Pond + Glavni + Stil hidranta: wsh + Podzemni + Ulica + Parking + Traka + Zelena + Trotoar + Kapacitet protoka hidranta + Broj hidranta + Pritisak hidranta + Prečnik hidranta + Kancelarija babice + Usluga nege + Kancelarija psihologa + Kancelarija iscelitelja + Kancelarija podologa + Kancelarija terapeuta + Lekarska ordinacija + SIDA: ne + SIDA: da + Autizam: ne + Autizam: da + Ebola: ne + Ebola: da + Malarija: ne + Malarija: da + Kućna poseta: ne + Da + Hitna: ne + Hitna: da + Savetovanje: ne + Savetovanje: da + Bolničke usluge: samo + Bolničke usluge: ne + Bolničke usluge: da + Predviđeno za dečake: ne + Predviđeno za dečake: da + Predviđeno za muškarce: ne + Predviđeno za muškarce: da + Predviđeno za devojke: ne + Predviđeno za devojke: da + Predviđeno za starije osobe: ne + Predviđeno za starije osobe: da + Predviđeno za žene: ne + Predviđeno za žene: da + Predviđeno za odrasle: ne + Predviđeno za odrasle: da + Predviđeno za decu: ne + Predviđeno za decu: da + Predviđeno za mališane: ne + Predviđeno za mališane: da + Predviđeno za novorođenčad: ne + Predviđeno za novorođenčad: da + Savetovanje (nasilje): ne + Savetovanje (nasilje): da + Savetovanje (žrtve): ne + Savetovanje (žrtve): da + Savetovanje (seksualno zlostavljanje): ne + Savetovanje (seksualno zlostavljanje): da + Savetovanje (seksualno): ne + Savetovanje (seksualno): da + Savetovanje (rehabilitacija): ne + Savetovanje (rehabilitacija): da + Savetovanje (ishrana): ne + Savetovanje (ishrana): da + Koralna obala + Donacija krvi + Medicinska laboratorija + Veleprodaja + Proizvodnja konditorskih proizvoda + Terensko prikupljanje + Uređaj inspekcije + Ventilski grupna + Ventilska + Merna + Kompresiona + Kompenzaciona + Pretvaračka + Vučna + Prelazna + Industrijska + Manja distribuciona + Distribuciona + Prenosna + Cevovodna podstanica + Jedino + Da + Popravka obuće + Brdo + Licencni časovi + Ruševine + Minsko polje + Opasnost od poplave + Klizav put + Opasnost od lavine + Opasnost od erozije + Nuklearna opasnost + Opasnost + Beskontaktno ne prihvata se + Beskontaktno + Izranjajuća stena + Prodavnica aparata + Krajnji datum + Stena + Kurs + Elektronski + Injektor + Kod + Tačka ureza + Pešačka kontrolna tačka + Prosečan nagib + Najniža tačka + Najviša tačka + Ledeni jezik + Ostatak + Plato + Stena + Ledopad + Viseći + Planinski + Plimski + Izlazni + Dolina + Plato + Ledeno polje + Ledena kapa + Težina + Broj kabla + Zaštićena ruta + Avantura penjanja + Zip linija + Gume + Osiguranje + Motor + Popravka transmisije + Reglaža + Prigušivač + Popravka kamiona + Staklo + Točkovi + Elektrika + Popravka karoserije + Klima uređaj + Baterije + Auto delovi + Dijagnostika + Prodaja novih automobila + Kočnice + Prodaja polovnih automobila + Promena ulja + Auto servis + Igrački centar za odrasle + Zabavna arkadna igra + Kancelarija za vodosnabdevanje + Mesto isporuke prodavnice + Ime rečnih brzaka + Keramika + Prodavnica podova + Nizak + Srednji + Visok + Nizak + Srednji + Visok + Nizak + Srednji + Visok + Nizak + Srednji + Visok + Nizak + Srednji + Visok + Nizak + Srednji + Visok + AS / NZS 3112 + BS 1363 + Šuko + NEMA 14-30 + NEMA 14-30 + NEMA 5-20 + NEMA 5-15R + Tesla Roudster + Tesla Superpunjač + Tesla standard + CHAdeMO + Tip 3 + Tip 2 kombo + Tip 2 + Tip 1 kombo + Tip 1 + CEE crvena 125A + CEE crvena 64A + CEE crvena 32A + CEE crvena 16A + CEE plava + Prodavnica kanabisa + Prodavnica čamaca + Prodavnica kamina + Zamrznuta hrana + Tip: pašnjak + Tip: trajna + Tip: prelazna + Prečnik krune + Obim + Penjačke rute + Brvno vrha penjanja: ne + Brvno vrha penjanja: da + Orijentacija zida: SZ + Orijentacija zida: Z + Orijentacija zida: JZ + Orijentacija zida: J + Orijentacija zida: JI + Orijentacija zida: I + Orijentacija zida: SI + Orijentacija zida: S + Fiksna sidra: ne + Fiksna sidra: da + Kvalitet penjanja: krh + Kvalitet penjanja: čvrst + Stena za penjanje: porfir + Stena za penjanje: gnais + Stena za penjanje: kvarcit + Stena za penjanje: peščenjak + Penjanje na kamen: granit + Stena za penjanje: krečnjak + Maksimalna dužina penjanja + Minimalna dužina penjanja + Dužina penjanja + Solo u dubljim vodama: ne + Solo u dubljim vodama: da + Mešovito: ne + Mešovito: da + Led: ne + Led: da + Rute na više tačaka: ne + Rute sa više tačaka: da + Tradicionalno: ne + Tradicionalno: da + Gornje uže: ne + Gornje uže: da + Balvaniranje: ne + Balvaniranje: da + Sport: ne + Sport: da + Poštanski broj + Transfer novca + Centar plaćanja + Stan + Kućni broj + Ulica + Kutija pisma + Depo + Stanica punjenje + Veličina mape: regija + Veličina mape: grad + Veličina mape: sajt + Tip mape: toposkopska + Tip mape: šema + Vrsta mape: ulična + Vrsta mape: topo + Izlaz stanice punjenja + Amperaža + Cena parkiranja + Cena parkiranja: ne + Cena parkiranja: da + Kamion: ne + Kamion: da + Skuter: ne + Skuter: da + Bicikl: ne + Bicikl: da + Automobil: ne + Automobil: da + Utičnica: AS / NZS 3112: izlaz + Utičnica: AS / NZS 3112: struja + Utičnica: AS / NZS 3112 + Utičnica: BS 1363: izlaz + Utičnica: BS 1363: struja + Utičnica: BS 1363 + Utičnica: Šuko: izlaz + Utičnica: Šuko: struja + Utičnica: Šuko + Utičnica: NEMA 14-50: izlaz + Utičnica: NEMA 14-50: struja + Utičnica: NEMA 14-50 + Utičnica: NEMA 14-30: izlaz + Utičnica: NEMA 14-30: struja + Utičnica: NEMA 14-30 + Utičnica: NEMA 5-20: izlaz + Utičnica: NEMA 5-20: struja + Utičnica: NEMA 5-20 + Utičnica: NEMA 5-15R: izlaz + Utičnica: NEMA 5-15R: struja + Utičnica: NEMA 5-15R + Utičnica: Tesla Roudster: izlaz + Utičnica: Tesla Roudster: struja + Utičnica: Tesla Roudster + Utičnica: Tesla superpunjač: izlaz + Utičnica: Tesla superpunjač: struja + Utičnica: Tesla superpunjač + Utičnica: Tesla standard: izlaz + Utičnica: Tesla standard: struja + Utičnica: Tesla standard + Utičnica: CHAdeMO: izlaz + Utičnica: CHAdeMO: struja + Utičnica: CHAdeMO + Utičnica: Tip 3: izlaz + Utičnica: Tip 3: struja + Utičnica: Tip 3 + Utičnica: Tip 2 kombo: izlaz + Utičnica: Tip 2 kombo: struja + Utičnica: Tip 2 kombo + Utičnica: Tip 2: izlaz + Utičnica: Tip 2: struja + Utičnica: Tip 2 + Utičnica: Tip 1 kombo: izlaz + Utičnica: Tip 1 kombo: struja + Utičnica: Tip 1 kombo + Socket: Tip 1: izlaz + Utičnica: Tip 1: struja + Utičnica: Tip 1 + Utičnica: CEE crvena 125A: izlaz + Utičnica: CEE crvena 125A: struja + Utičnica: CEE crvena 125A + Utičnica: CEE crvena 64A: izlaz + Utičnica: CEE crvena 64A: struja + Utičnica: CEE crvena 64A + Utičnica: CEE crvena 32A: izlaz + Utičnica: CEE crvena 32A: struja + Utičnica: CEE crvena 32A + Utičnica: CEE crvena 16A: izlaz + Utičnica: CEE crvena 16A: struja + Utičnica: CEE crvena 16A + Utičnica: CEE plava: izlaz + Utičnica: CEE plava: struja + Utičnica: CEE plava + Flaširana voda + Rezervoar za vodu + Prevoz vode + Bušotina + Pumpa + Tekuća voda + Cevovod + Bunar + Akvatablete + Obrnuta osmoza + Hlor + Nema + Trajnost vodenog mesta: hitno + Trajnost vodenog mesta: izdržljiva + Potrebno održavanje + Prekinuto + Ograničen + Lokacija: kiosk + Iznajmljivanje čamaca + Kreveti + Rezervacija: samo članovi + Rezervacija: ne + Rezervacija: da + Rezervacije: preporučuje se + Rezervacija: obavezna + Zimska soba: ne + Zimska soba: da + Pravac: svi + Pravac: izlaz + Pravac: ulaz + Pravac: nadole + Pravac: nagore + Pravac: suprotno od kazaljke na satu + Pravac: u smeru kazaljke na satu + Pravac: unazad + Pravac: napred + Pravac: sever-severozapad + Pravac: severozapad + Pravac: zapad-severozapad + Pravac: zapad + Pravac: zapad-jugozapad + Pravac: jugozapad + Pravac: jug-jugozapad + Pravac: jug + Pravac: jug-jugoistok + Pravac: jugoistok + Pravac: istok-jugoistok + Pravac: istok + Pravac: istok-severoistok + Pravac: severoistok + Pravac: sever-severoistok + Pravac: sever + Svemirska luka + Puštanje: ne + Puštanje: da + Usvajanje: ne + Usvajanje: da + Vlasnik + Standardni + Dualsport + Van puta + Čoper + Sportski motorcikl + Skuter + Moto odeća: ne + Moto odeća + Gume: ne + Gume + Delovi: ne + Delovi + Popravaka: ne + Popravka + Iznajmljivanje: ne + Iznajmljivanje + Da + Oznaka zemlje + Prirodni spomenik + Briga o deci + Putarina + Štit + Stratovulkan + Šljaka + URL + Parking mesto + Grob + Pol zajednice: mešoviti + Pol zajednice: muški + Pol zajednice: ženski + Turistički kamp + Tip manastira: regularno službeni + Tip manastira: otšelnički + Tip manastira: kanonski + Tip manastira: muški + Tip manastira: ženski + Ne + Da + Kupatilo za stopala + Jezero + Reka + Termalno + Tursko kupatilo + Onsen + Topli izvor + Poštanska banka + Žirokartica + Migros banka + Postfinansijska kartica + Podizanje gotovine: strane kartice + Podizanje gotovine: minimalna kupovina + Naknada za podizanje gotovine: ne + Naknada za podizanje gotovine: da + Podizanje gotovine: kupovina nije neophodna + Podizanje gotovine: kupovina neophodna + Valuta za podizanje gotovine + Limit za podizanje gotovine + Tip podizanja gotovine: samoplaćanje + Tip podizanja gotovine: kasa + Operator podizanja gotovine + Podizanje gotovine + Podizanje gotovine: da + Šišanje ljubimaca + Punjenje + Stolar ormana + Pekara + Parketar + Stolar + Destilerija + Zidar + Zdrava hrana + Ulaz u podrum + Određeno + Da + Određeno + Da + Isporuka + Određeno + Dopušteno + Odredište + Da + Određeno + Da + Određeno + Da + Određeno + Odredište + Dopušteno + Da + Određeno + Da + Određeno + Da + Određeno + Da + Određeno + Da + Dopušteno + Određeno + Da + Pristup treneru: ne + Pristup autobusu: da + VJS pristup: ne + PSV pristup: određen + VJS pristup: da + Pristup motornim kućama: ne + Mofa pristup: ne + Taksi kancelarija + Salon sa nargilom + Gomila uglja + Naknada promene stola: ne + Naknada promene stola: da + Broj promena stola + Lokacija promene stola: uniseks toalet + Lokacija promene stola: ženski toalet + Lokacija promene stola: muški toalet + Lokacija promene stola: soba + Promena stola: ograničena + Promena stola: ne + Promena stola: da + Lokacija + Opasnost: kontaminacija + Opasnost: minsko polje + Opasnost: lavina + Opasnost: poplava + Opasnost: nuklearna + Opasnost: odron stena + Opasnost: erozija + Spasilačka kutija + Izbrisani objekt + Gasna baklja;Gomila baklji + 3B* + 3B + 3A* + 3A + 2B* + 2B + 2A* + 2A + 1B* + 1B + 1A* + 1A + n/k* + n/k + Trajekt + Tramvaj + VOV + Metro + Autobus + Voz + Gradski blok + Gradić + Poklon kutija + Da + Da + Vibracija: ne + Status pumpe: nedostaje snop + Usisni + Pod pritiskom + Podzemna voda + Cev + Mreža za ponovno dolivanje pitke vode + Dolivanje vode za piće: ne + Da + Opstrukcija + Nivo vode: ispod srednjeg nivoa + Nivo vode: iznad srednjeg nivoa + Nivo vode: plutajući + Nivo vode: poplave + Nivo vode: na nivou + Nivo vode: prekrivači + Nivo vode: suv + Nivo vode: potopljen + Nivo vode: delimično potopljen + Neispravano + Primitivno + Kontrastno + Dozvoljeno je samo pri hodanju + Pristup internetu: kupci + Ne + Da + Tip kabine + Kabina + Ne + Da + Ronilački centar + Lovačka baza + Referentni broj staze + Centar za kuglanje + Prodavnica bezbednosti + Planinska spasilačka + Paintbol + Bicikl duh + Broj erupcija + Neaktivan + Aktivan + Latentan + Izumreo + Poslednja erupcija + Blato + Kupola lave + Kaldera + Mar + Strelica: ne + Tabla odlaska: ne + Ne + Da + Ne + Da + Ne + Da + Podnacionalni + Predstavništvo + Kancelarija + Počasni konzulat + Generalni konzulat + Konzularna kancelarija + Konzularna agencija + Na čelu sa konzulom + Rezidencija + Nuncijatura + Misija + Interesni + Visoka komisija + Delegacija + Podružnica + Na čelu sa ambasadorom + Posrednik + Ambasada + Radarski toranj + Parking + Parking + Tavan + GPKS tačka + LNG + Prodavnica oraha + Košnica + Raspored + Stvarno vreme + Kašnjenje + Da + Lift + Mali električni uređaji + Usluge za građane + Imigrantske vize + Neimigrantske vize + Posrednik + Konzulat + Ambasada + Tabla + Dolivanje vode za piće + Baza spasioca + Sirena + Medicinska sestra + Mobilni agent za novac + Jezero + Reka + Cepljenje: Kovid 19 + Cepljenje + Društveni klub + Plato + Diplomatsko predstavništvo + Kik-boks + Mačevanje + Kurling + Krosfit + Tuča petlova + Borba sa bikovima + Bob + Biatlon + Aikido + Vodeno skijanje + Vaterpolo + Rvanje + Dizanje tegova + Vejkbording + Ultimat + Taekvondo + Futsal + Sumo + Snuker + Stanje staze: otvorena + Obilažena: ne + Obilažena: da + Ime staze + Ski skok + Tunel za slepe miševe + Most za slepe miševe + Prelaz za divljač + Deo za plivanje + Pojilo + Stanica za prenos otpada + Kolska vaga + Šumarska stanica + Cevni bunar + Bunar + Električna pumpa + Tank sa vodom + Česma + Tip zaliva + diff --git a/OsmAnd/res/values-b+sr+Latn/strings.xml b/OsmAnd/res/values-b+sr+Latn/strings.xml index 209c3f8e1a..174e5b2515 100644 --- a/OsmAnd/res/values-b+sr+Latn/strings.xml +++ b/OsmAnd/res/values-b+sr+Latn/strings.xml @@ -1,193 +1,195 @@ - + + Verzija: Verzija info, licence, članovi projekta - Izaberite da li da se čuje zvuk prilikom snimanja fotografija -Fokus je podešen na beskonačno - Makro režim fokusa - Auto fokus - Obriši sve - Prvo izračunajte putanju + Postavite da li se čuje zvuk prilikom škljocanja ili ne. + Fokus je podešen na beskonačno + Makro režim fokusa + Autofokus + Obriši sve + Najpre izračunajte putanju Simulacija pomoću izračunate putanje Simulacija pomoću GPX putanje - Zapamti moj izbor + Zapamti izbor i ili - Bez auto-zuma - Osnovna karta sveta - Ističe(minuta): %1$s + Bez auto-zuma + Osnovna karta sveta + Ističe za (minuta): %1$s Maksimalno uveličanje: %1$s - Ekliptični prikaz zemljine kugle + Eliptična Merkator projekcija Maksimalno uveličanje Minimalno uveličanje URL Izaberite postojeći… Podaci o greškama brzine prikaza sličica - Japan - Sjedinjene Američke Države - Kanada - Evropa i Azija - Velika Britanija, Indija, Australija & Ostalo - Minimalni zum: %1$s + Japan + Sjedinjene Američke Države + Kanada + Evropa, Azija, Južna Amerika i slične + Velika Britanija, Indija i slične + Minimalni zum: %1$s Najavi… - Podešavanje najave naziva ulica, saobraćajnih upozorenja, ograničenja brzine - Obaveštenje - naziv ulice - Obaveštenje - ograničenje brzine - Obaveštenje - kamera - Obaveštenje - upozorenja u saobraćaju - Definišite OSM korisnika i lozinku u podešavanjima - Obriši međutačke - Sačuvaj međutačke - Već imate definisane međutačke. - Uputstvo do - Uputstvo od + Podesite najave naziva ulica, saobraćajnih upozorenja (ležeći, znaci zaustavljanja), radara i ograničenja brzine. + Nazivi ulica (TTS) + Ograničenje brzine + Kamere + Upozorenja u saobraćaju + Definišite OSM korisničko ime i lozinku u „Podešavanjima“ + Obriši prolazna odredišta + Sačuvaj prolazna odredišta + Već imate podešena usputna odredišta. + Navođenje do + Navođenje od Karta : Šir %1$.3f, duž %2$.3f Do: Preko: Iz: - Pregledaj mapu - Primarni profil - Destinacija %1$s + Pregled karte + Primarni profil + Destinacija %1$s Postavi kao destinaciju - Izaberite prvo grad ili ulicu + Postavite prvo grad ili ulicu Obriši tačku vreme - preciznost + tačnost brzina visina Tačka - naziv GPX fajla - GPX datoteka uspešno sačuvana na {0} - Kalkulator rastojanja i Alat za planiranje + Naziv GPX fajla + GPX fajl sačuvan u {0} + Kalkulator rastojanja i alat za planiranje Ne prikazuj ponovo Počnite uređivanje Završi uređivanje Obrišite sve tačke - Otvori postojeći GPX + Otvori postojeći GPX fajl Molimo sačekajte dok se ne završi trenutni zadatak Koristite Kalman filter Koristi magnetni senzor Ostalo %1$d fajlova ostalo - Dostupno %1$d datoteka za preuzimanje + %1$d fajlova preostalo da se preuzme Puna verzija - Konfigurisanje audio i video podešavanja + Podesite audio i video. Audio/video podešavanja - Došlo je do greške prilikom snimanja - Kamera nije dostupna - Da li želite da uklonite ovaj snimak ? + Snimanje nije uspelo + Kamera nedostupna + Izbrisati ovu stavku\? nedostupan Napravi audio belešku Napravi video belešku Sloj za snimanje Obriši zapis Reprodukuj - snimanje + Snimanje Počni Audio/video beleške Merenje udaljenosti Audio beleške - Ostale mape + Ostale karte Samo putevi - izohipse - Sakrij granice + Izohipse + Granice Ograničenje brzine Nema pronađenih objekata. Traži grad inkrementalno - Pretraga sela/Poštanski kod + Pretraga za još sela/poštanskih kodova Nativna biblioteka nije podržana na ovom uređaju . - Pokretanje nativne biblioteke… - Autocentriranje mape + Pokretanje nativne biblioteke… + Autocentriranje karte Podešavanje vektorske mapa - Predgrađe - Selo - Grad - Zaustavi simulaciju - Započni simulaciju - Fajl ne može biti preimenovan . - Fajl sa tim imenom već postoji. - Pretraga po imenu - Mesna datoteka za održavanje promene POI-a nije pronađena i ne može da se napravi . - Nadogradite Osmand+ - Server sadrži mape datoteke koje nisu u skladu sa trenutnim izdanjem aplikacije. Da biste ih preuzeli i koristili, molimo Vas da, nadogradite aplikaciju na novije izdanje. - Preimenovanje + Predgrađe + Selo + Grad + Zaustavi simulaciju + Započni simulaciju + Ne mogu da preimenujem fajl. + Fajl sa tim imenom već postoji. + Pretraga po imenu + Lokalni fajl koji održava promene tačaka od interesa nije pronađen i ne može da se napravi. + Nadogradite OsmAnd+ + Preuzmite novu verziju ove aplikacije da biste mogli da koristite nove fajlove karata. + Preimenuj Online Nominatim - Adresa … - Neodređeno + Adresa… + Neodređeno Konfiguracija ekrana - Prikaži trake - Izbegavaj neasfaltirane puteve - Izbegavaj trajekte - Fluorescentne rute - Omogući\n režim spavanja - Naziv ulice - Podešavanje ekrana - Gde sam - Zaključaj ekran - Kompas + Trake + Bez neasfaltiranih puteva + Bez trajekata + Fluorescentne rute + Pokreći +\n aplikaciju u pozadini + Naziv ulice + Podešavanje ekrana + Gde sam + Zaključaj + Kompas Vrati na podrazumevano Brzina - Destinacija - Visina - Sledeće skretanje - Sledeće skretanje (malo) - Drugo sledeće skretanje - Zaključaj ekran - Otključaj ekran - Ekran je zaključan - ukucajte za filtriranje - Ekran visoke rezolucije - glas - Vektorske mape nisu učitane - GPX datoteke se ne nalaze u direktorijumu - Greška pri čitanju GPX podataka + Odredište + Visina + Sledeće skretanje + Sledeće skretanje (malo) + Drugo sledeće skretanje + Zaključaj + Otključaj + Ekran je zaključan + ukucajte za filtriranje + Ekran visoke rezolucije + Snimljeni glas + Vektorske karte nisu učitane + Nema GPX fajlova u fascikli staza + GPX podaci ne mogu da se pročitaju. Izmena POI - Brisanje POI - Bez rotacije (sever je gore) - Orjentacija mape + Brisanje POI + Bez rotacije (sever je uvek gore) + Orijentacija karte Lenjir Uvezi - Došlo je do greške prilikom učitavanja GPX - Pošalji izveštaj - Nema podataka za datu oblast na SD kartici. Preuzmite podatke sa Interneta. - Bilo koji - Ruta - Auto-centriranje karte samo tokom navigacije. - Auto-centriranje karte tokom upotrebe. - Izlazak sunca: %1$s \nZalazak sunca: %2$s - Dan/noć informacije - Stil mape + GPX ne može da se učita. + Pošalji izveštaj + Skinute karte na memorijskoj kartici nisu nađene. + Bilo koji + Ruta + Auto-centriranje karte samo tokom navigacije. + Auto-centriranje karte tokom upotrebe. + Izlazak sunca: %1$s \nZalazak sunca: %2$s + Dan/noć informacije + Stil karte Izbegavati… - Došlo je do greške tokom renderovanja izabrane oblasti - Koristi lokaciju… - Render uspešno učitan - Greška: render nije bio učitan - Vektorski renderer - Odaberite poravnavanje ekrana mape - Detalji rute - Uneti upit za pretragu POI - Dnevnik grešaka OSM-a na mreži - POI… - Pretraga POI - Gde sam ja? - OsmAnd navigacioni servis - Mreža - GPS - sekunde - min. + Ne mogu da iscrtam izabranu oblast. + Koristi lokaciju… + Render učitan + Renderer ne može da se učita. + Vektorski renderer + Poravnavanje karte: + Detalji rute + Kucaj da pretražuješ tačke od interesa + OSM beleške (na mreži) + Sloj sa tačkama od interesa… + Pretraga tačaka od interesa + Gde sam ja? + OsmAnd navigacioni servis + Mreža + GPS + sekunde + min. nijedan Da li ste sigurni da želite da prekinete navigaciju? Prikaži - Fotografija %1$s od %2$s + Fotografija %1$s %2$s Slikaj Slikaj - Dropbox dodatak omogućava da sinhronizujete putanje i audio/video beleške sa vašim Dropbox nalogom. + Sinhronizujete putanje i audio/video beleške sa vašim Dropbox nalogom. Dropbox dodatak Promeni redosled Podrazumevano Pešački prelaz - Prikazuj pešačke prelaze - Da li želite da preuzmete karte za korišćenje van mreže? + Pešački prelazi + Preuzeti karte za korišćenje van mreže\? Preuzeli ste %1$s karata Preuzmi novu kartu Upravljajte @@ -195,160 +197,3897 @@ Oblast Sva preuzimanja Nadogradnje - Mesno - Preuzimanje je nemoguće, proverite vezu sa Internetom. + Lokalno + Preuzimanje nemoguće, proverite vezu sa Internetom. Otkaži - Sve datoteke su sveže + Svi fajlovi su sveži Koristi OpenGL za prikaz - Koristi hardversko ubrzanje OpenGL-a za prikaz (možda ne radi na nekim uređajima) + Koristi hardversko ubrzanje OpenGL-a za prikaz (može da koristi više baterije, a može i da ne radi na nekim uređajima). Dostupne su nadogradnje %1$s karata - Potraži - PRIKAŽI SVE - Koordinate - Nikad + Pretraga + Prikaži sve + Koordinate + Nikad Sakrij - Pojedinosti + Detalji Pretraga položaja… - Položaj (pronađen) - Omiljeno… - Središte trenutne mape - Izvor: - Traži obližnje - "Ime datoteke: " - Datoteka sa istim imenom već postoji. - Sačuvaj - Otpremite GPIks datoteke zajednici OSM. Upotrebiće se za poboljšanje mapa. - %1$d od %2$d stavki je uspešno otpremljeno. - Pošalji OSM-u - Prikaži više pojedinosti mape - Omiljena tačka(e) je uspešno izbrisana. - Prijatelji - Mesta + Moj položaj (pronađen) + Omiljeno… + Centar trenutne karte + Izvor: + Traži u blizini + Ime fajla: + Fajl sa istim imenom već postoji. + Sačuvaj + Otpremite GPX fajlove OSM zajednici, i tako poboljšajte karte. + %1$d od %2$d stavki otpremljeno. + Pošalji OSM-u + Prikaži više detalja na karti + Omiljena tačka(e) izbrisana. + Prijatelji + Mesta Ime - Vrsta - Osnovna mapa sveta (koja pokriva ceo svet na malom uveličanju) nedostaje. Molim, preuzmite World_basemap_x.obf radi celokupnosti sredine. - Izdanje - Nema stavki za %1$s - Upravljajte datotekama mapa - Pokreni - Isključi + Kategorija + Preuzmite osnovnu mapa sveta da dobijete pregled celog sveta na malom uveličanju. + Lokalno izdanje + Nema stavki za %1$s + Upravljajte fajlovima karata. + Pokreni + Isključi Preuzmi - Podaci POI-a - Podaci adresa - Podaci javnog prevoza - Podaci mape - Isključeno - Glasovni podaci (TTS) - Glasovni podaci (snimci) + Podaci POI-a + Podaci adresa + Podaci javnog prevoza + Podaci karte + Isključeno + Glasovna navođenja (TTS) + Glasovna navođenja (snimljena) Podaci POI-a - Glas TTS-a - Nova pretraga - Izaberite veličinu pisma za imena na mapama - Veličina pisma na mapi - Podaci o otklonu grešaka prikaza - Prikazuj učinak prikaza + Glas TTS-a + Nova pretraga + Veličinu teksta za imena na kartama: + Veličina teksta na karti + Podaci o otklonu grešaka prikaza + Prikazuj učinak prikaza. Raspakujem nove podatke… - Izabrana je usluga navođenja preko mrežne veze, ali je mrežna veza nedostupna. - Jezik nije podržan - Nedostaju podaci - Nema ugrađenih podataka izabranog jezika. Da li želite da odete do prodavnice radi ugradnje? - Suprotan pravac GPIks-a - Izlaz navođenja glasom - Zvuk govornog poziva - Zvučno obaveštenje + Navigacija preko interneta ne radi kad niste na internetu. + Jezik nije podržan + Nedostaju podaci + Otići do prodavnice da skinete izabrani jezik\? + Obrni smer staze + Izlaz glasovnog navođenja + Zvuk dolaznog poziva (prekida i blutut u kolima) + Zvučno obaveštenje %1$s zahteva ovlašćenja da isključi ekran radi uštede energije. Uključi ekran - Uključi ekran uređaja (ako je isključen) kada se približi skretanje - Ostala svojstva mape - Preostali činioci + Uključi ekran uređaja (ako je isključen) kada se približi skretanje. + Ostala svojstva karte + Ostali elementi Svojstva prikaza Traka stanja - Desna površ - Leva površ + Desna površ + Leva površ Prikaži - Podesite mapu - Bezimeni korisnik ne može\n- da pravi skupove;\n- da usklađuje skupove i uređaje sa serverom;\n- da upravlja skupovima i uređajima u ličnom uredu. + Podesi kartu + Anonimni korisnici ne mogu: +\n- da prave grupe; +\n- da sinhronizuju grupe i uređaje sa serverom; +\n- da upravljaju grupama i uređajima na svojoj komandnoj tabli na sajtu. Bezimeni korisnik Prijavljen kao %1$s - Dopušteno prekoračenje brzine - Granica dopuštenog prekoračenja brzine, iznad koje ćete dobiti glasovno upozorenje. - Ime omiljene tačke je izmenjeno u %1$s da bi se olakšalo pravilno čuvanje niske sa znakom osećanja u datoteku. - Ime omiljene tačke je udvostručeno - Izmenio sam ime Vaše omiljene tačke u %1$s radi izbegavanja istih imena za više tačaka. - Podesi veličinu pisma na mapi. - Veličina pisma + Dopušteno prekoračenje brzine + Odaberite granicu dopuštenog prekoračenja brzine, preko koje ćete dobiti glasovno upozorenje. + Omiljena tačka preimenovana u %1$s da bi se sačuvao tekst koji sadrži emodžije u fajl. + Dat duplikat imena omiljene tačke + Omiljeno ime je izmenjeno u %1$s da se izbegnu ista imena. + Podesi veličinu teksta na karti. + Veličina teksta Ograničenje brzine Znak stop - Najavljuj prešačke prelaze - Vrsta puta - Možete da izaberete zamensku putanju izborom puteva za izbegavanje - Da li započeti navođenje po stazi? - Narandžasta + Pešački prelazi + Tip puta + Izaberete alternativne putanje izborom puteva za izbegavanje + Da započnem navođenje duž putanje\? + Narandžasta Pružni prelaz - Prikazuj pružne prelaze - Osvetljenje ulice - Mrežni posrednik - Podesite mrežnog posrednika za internet + Pružni prelazi + Osvetljenje ulice + Mrežni proksi + Navedite proksi server. Privatnost Bez usmeravanja po izdanju v1.9 - Nemoj da koristiš pravila usmeravanja iz izdanja v1.9 + Nemoj da koristiš pravila usmeravanja iz izdanja v1.9. Stanice Geografska širina Geografska dužina Nisam pronašao zaobilaznice Preimenovanje nije uspelo. - Staza koja se trenutno snima - Nazad na kartu - Prelazak pregleda karte prikazuje pomorski izgled, čime se prikazuju sve pomorske oznake navođenja i grafički znaci. - \n\nDatoteka karte koja sadrži sve opšte pomorske znake je dostupna u vidu jednog preuzimanja i naziva se „Svetske pomorske karte“. - \n\nPregled može da se vrati na stari njegovim isključivanjem ovde, ili izmenom „Načina prikaza karte“ pod „Podesite kartu“. - - Pokretanje ove izmene menja pregled karte na „Zima i skijanje“, koje prikazuje u položenom pogledu osobine predela i zimske uslove. - \n\nSvetska karta prikazuje sva skijališta i skijaške staze u ovom pregledu je dostupna preuzimanjem „Svetske skijaške karte“. - - Zvuk + Staza koja se trenutno snima + Nazad na kartu + Ovaj dodatak obogaćuje OsmAnd aplikaciju da prikazuje i pomorske karte za vožnju brodova, jedrenje i druge tipove vodenih sportova. +\n +\nFajl karte koja sadrži sve opšte pomorske znake je dostupna u vidu jednog preuzimanja i naziva se „Svetske pomorske karte“. +\n +\nPregled može da se vrati na stari njegovim isključivanjem ovde, ili izmenom „Načina prikaza karte“ pod „Podesite kartu“. + Ovaj OsmAnd dodatak Vam pod prste stavlja detalje globalnih ski padina, kros kantri trka, alpskih ski ruta, žičara i ski liftova. Rute i piste su prikazane obojene po težini i nalaze se u specijalnom stilu karte zvanom \"Zima i skijanje\" koja vizuelno i podseća na zimske pejzaže. +\n +\nAktiviranjem ovog pregleda će se promeniti stil karte na „Zima i skijanje“ koja će prikazivati sve odlike oblasti u zimskih uslovima. Ovo može da se vrati ili tako što se deaktivira ovaj dodatak ovde, ili tako što se promeni stil karte na „Podesi kartu“ na novi, željeni stil. + Zvuk Deli belešku Geografski položaj:\nŠirina %1$s↵\nDužina %2$s - Pregledaj - Beležnica - Karta sa mreže - Samo putevi - "Slobodno %1$s " - Skladište uređaja + Pregledaj + A/V beleške + Karta sa mreže + Samo putevi + Slobodno %1$s + Skladište uređaja Pregled skijaške karte Pregled pomorske karte - Da bi se prikazivala skijaška karta, naročita karta za pregled van mreže treba da se preuzme - Da bi se prikazivale pomorske karte, naročita karta za pregled van mreže treba da se preuzme - Uredi skup + Preuzmite posebnu kartu van mreže da bi prikazali skijašku kartu. + Preuzmite posebnu kartu van mreže da bi prikazivali pomorske detalje. + Uredi skup UKLONI OZNAKU Stanje GPS-a - Preuzmi noćne izgradnje + Preuzmi noćne izgradnje. Izgradnje Tačke Moj položaj Kuća Linije metroa U blizini -Gustina linija - Gustoća linija - Velika - Srednje - Mala - Širina konturnih linija - Širina konturnih linija - Voda - Sakriti vodu - Omogućiti autoputeve - Navigacija - Konfiguracija mape - Prikazati %1$s - Sakriti %1$s - Molimo dodajte barem jednu tačku. - GPX ime datoteke: - Prikazati na karti nakon snimanja - Pretražite mapu i dodajte tačke - Mera za daljinu - Nastaviti/pauzirati navigaciju - Dodirnite ovo dugme da biste zaustavili ili nastavili navigaciju. - Prikazati navigacioni završni dijalog - \ No newline at end of file + Gustina linije izohipse + Gustina linije izohipse + Gusta + Srednja + Retka + Debljina linije izohipse + Debljina linije izohipsi + Vode + Sakrij vodu + Koristi autoputeve + Navođenje + Podesite kartu + Prikaži %1$s + Sakrij %1$s + Molim dodajte bar jednu tačku. + Ime GPX fajla: + Prikaži na karti posle čuvanja + Pregledaj kartu i dodaj tačke + Lenjir + Pauziraj/Nastavi navođenje + Dugme za pauziranje ili nastavak navođenja. + Prikaži dijalog da je navođenje završeno + Vremena glasovnih navođenja + ’Slobodna vožnja’ i ’Van staze’ su nezvanične rute i prolazi. Uglavnom su nesređene, neodržavane i ne proveravaju se uveče. Ulazite na sopstvenu odgovornost. + Van staze + Preferiraj staze ove težine, ali ostavi moguće navođenje preko težih ili lakših staza ako je kraće. + Željena težina + Staze pripremljene za klasični stil bez puteva za klizanje. Ovo uključuje i state pripremljene malim vozilima za sneg i putanjama koje su skijaši napravili. + Dozvoli samo klasične puteve + Putevi spremljeni za slobodni stil ili samo klizanje bez klasičnih putanja. + Dozvoli puteve samo za klizanje + Užasno teški putevi, sa opasnim preprekama i okolinom. + Dozvoli ekspertske puteve + Teži putevi, sa opasnim preprekama i strmim delovima. + Dozvoli napredne puteve + Teži putevi sa strmijim delovima. Generalno gledano, neke prepreke koje treba izbegavati. + Dozvoli usputne puteve + Padine za sankanje. + Sanke + Putanje ski tura. + Ski ture + Putevi za nordijsko ili kros-kantri skijanje. + Kros-kantri i nordijsko skijanje + Padine alpskog skijanja ili spusta i pristup ski liftovima. + Alpsko i spust skijanje + Dodajte bar jednu stavku na postavkama u „Brzim radnjama“ + Izbegavaj kaldrme + Bez kaldrma + Pokreni OsmAnd\? + Koristite kartu {0} koju je omogućio OsmAnd. Da li želite da pokrenete OsmAnd punu verziju\? + Nagib + Uglačanost + Podloga + Tip puta + Kliknite dugme i slušajte odgovarajuće glasovno navođenje da čujete da li je mu nešto nedostaje ili je pogrešno + Kako da otvorite članke sa Vikipedije\? + Članak nije nađen + Pretražujem odgovarajući viki članak + ova regija + Pogledaj članak u veb čitaču. + Otvori članak na internetu + Preuzmi podatke sa Vikipedije + Preuzmite Vikipedija članke za %1$s da biste mogli da ih čitate van mreže. + Tuneli + Tunel ispred + Položaj bez imena + Kopiraj poziciju/ime tačke + Pomorsko + Turistički pregled + Zima i skijanje + isklj. + Ukupno donacija + OSM primaoci + Pomeri tačku + Fajl %1$s ne sadrži prolazne tačke, da ga uvezem kao putanju\? + Uvezi GPX fajl + Uvezite Omiljene tačke ili ih dodajte označavajući ih kao oznake na karti. + Dodaj omiljene + Tačka puta 1 + Prolazna tačka 1 + Sačuvaj tačku puta + Sačuvaj GPX prolaznu tačku + Dodaj liniju + Dodaj prolaznu tačku + Dodaj tačke puta + Pokušaj ponovo + Slike sa Mapillary-ja je moguće videti samo ako ste povezani na internet. + Ime radnje + Dugo držanje i prevlačenje dugmeta ga pomera po ekranu. + Pomeri dugme + Izvor karte promenjen na „%s“. + Dodaj izvor karte + Dodaj izvor karte + Izmeni izvor karte + Dodaj sloj ispod karte + Slojevi ispod karte + Izmeni sloj ispod karte + Sloj ispod karte promenjen na „%s“. + Sloj iznad kartom promenjen na „%s“. + Dodaj sloj nad kartom + Slojevi iznad karte + Izmeni sloj nad kartom + Stilovi karte + Popunite sve parametre + Dodaj stil karte + Dugme za prolaženje kroz spisak ispod. + Dodajte jednu ili više kategorija tačaka od interesa koje će biti prikazivane na karti. + Spisak tačaka od interesa + Izaberite opcionu kategoriju. + Kategorija u koju da se čuvaju Omiljene: + Poruka + Ova poruke je uključena u polje sa komentarima. + Ostavite prazno da se koristi adresa ili ime mesta. + Napravi stavke + Dodaj kategoriju + Dugme za prikazivanje i sakrivanje tačke od interesa na karti. + Dugme za prikazivanje i sakrivanje Omiljenih tačaka na karti. + Već postoji brza radnja sa ovim imenom + Brza radnja preimenovana u %1$s da se izbegne dupliranje. + Mesto + " sačuvano u " + Prikaži dijalog međuvremena + Menjaj dnevni/noćni režim + Noćni režim + Dnevni režim + Dugmeta da menja dnevni i noćni režim u OsmAnd-u. + Dugme za dodavanje parking mesta na sredinu ekrana. + Dugme da uključi ili isključi glasovno navođenje. + Dugme za dodavanje tačke od interesa na sredinu ekrana. + Dugme za dodavanje OSM beleške na sredinu ekrana. + Dugme za dodavanje slikovne beleške na sredinu ekrana. + Dugme za dodavanje video beleške na sredinu ekrana. + Dugme za dodavanje zvučne beleške na sredinu ekrana. + Dugme za dodavanje GPX prolazne tačke na sredinu ekrana. + Dugme za dodavanje oznake na karti na lokaciji na centru ekrana. + Unapred postavljeno ime + Prikaži dijalog za Omiljene + Da li ste stvarno želite da obrišete radnju „%s“\? + Obriši radnju + Dodaj radnju + Dodaj Omiljeno + Uredi akciju + Dodaj akciju + Dodaj parking mesto + Isključi glas + Uključi glas + Glas uklj/isklj + Dodaj OSM belešku + Nova slikovna beleška + Nova video beleška + Nova zvučna beleška + Stil karte je izmenjen na „%s“. + Izmeni stil karte + Dodaj tačku od interesa + Dodaj oznaku na karti + Ekran %d + Radnja %d + Tip tačke od interesa + Dodaj vremena otvaranja + Kontakt informacije + Zatvara se u + Otvara se u + Sledeće + Broj zgrade + Napredno + Osnovno + Predaj tačku od interesa + Karte van mreže +\n& Navođenje + OsmAnd + Preskoči + Poništi + Kartica je sakrivena + Podešavanje komandne table + Greška: {0} + Pokušajte ponovo + Otpremljeno {0}/{1} + Obriši izmenu + Ne mogu da otpremim + Rcite nam Vaše predloge. + Recite nam zašto. + Molimo dajte OsmAnd-u ocenu na Google Play prodavnici + Ocenite ovu aplikaciju + Važno nam je da čujemo Vaše mišljenje. + Da li Vam se OsmAnd sviđa\? + Pmaetno preračunavanje puta + Preuzmi + Morate imati internet da biste instalirali ovaj dodatak. + Neispravni format: %s + Beličinu slike ugrađenog fotoaparata + Veličina slike fotoaparata + Pusti zvuk škljocanja + Fotoaparat neprestano pokušava da fokusira + Produžena dubina polja (EDOF) + Hiperfokalni fokus + Način fokusa fotoaparata: + Tip fokusa fotoaparata + e-pošta + Filter \'%1$s\' napravljen + Filter \'%1$s\' izbrisan + Obriši ovaj filter\? + Sačuvaj kao + Filter + Predaj + Sve druge oznake su očuvane + Tačka od interesa se menja + Ponovo otvori + Komentar + Otvoreno + Informacije o čvoru nisu učitane + I/O greška tokom izvođenja radnje {0}. + Greška prilikom izvođenja radnje {0}. + Radnja {0} izvršena. + obriši + izmeni + dodaj + Izbrisana + Obriši tačku od interesa + Obriši {0} (komentar)\? + Napravi tačku od interesa + Izmeni tačku od interesa + Omiljena tačka {0} izbrisana. + Izbriši Omiljenu tačku \'%s\'\? + Izbriši Omiljenu + Izmeni Omiljenu + Dodaj Omiljenu + Omiljena tačka „{0}“ dodata. + Omiljena + Unesi ime Omiljene tačke + Napravi tačku od interesa + Ažuriraj kartu + Raskrsnica ulica + Zgrada + Zgrada + Ulica + Grad + Region + Adresa + DDD MM SS.S + DDD MM.MMM + DDD.DDDDD + Geografska dužina + Geografska širina + Unesite širinu i dužinu u odabranom formatu (D - stepeni, M - minuti, S - sekunde) + Na dnu + Na sredini + Pešačenje + Bicikla + Vožnja + Najbliže usluge + Odaberite ulicu koja je seče + Odaberite oblast sa spiska + Priraštajno traži zgrade + Priraštajno traži ulice + Nađi još + Izaberite tačke od interesa + Traži + Traži + Aplikacija za navođenje + Skidajte sličice koje nedostaju + Prikaži GPS koordinate na karti + Prikaži Vašu poziciju + Koristi internet + Izvor karte + Izvor sličice karte + Izaberite izvor mrežnih ili zahvaćenih pločica karata. + Prikaži sloj sa tačkama od interesa iznad karte + Prikaži poslednje korišćeni sloj sa tačkama od interesa. + 3D prikaz karte + Uključi 3D prikaz karte. + Prikaži smer gledanja + Izaberite državu + Izaberite grad ili poštanski broj + Izaberite ulicu + Izaberite zgradu + Traži adresu + Postavke aplikacije + Koristi engleska imena na kartama + Izaberite između lokalnih ili engleskih imena. + Odredište + Ponovo učitaj sličicu + Ažuriraj kartu + Dodatne postavke + Podaci + Odaberi jezik, preuzmi/ponovo učitaj podatke. + Odredite OpenStreetMap.org (OSM) postavke potrebne za slanje na OSM. + Koristite mrežno navođenje + Koristite internet da izračunate put. + Gotovo + Čuvam GPX fajl… + OsmAnd se srušio na prošlom pokretanju. Zapisnik je u {0}. Molimo prijavite problem i zakačite ovaj fajl. + Čitanje lokalnih podataka… + Učitavanje podataka… + Vrati se na kartu + Neispravne koordinate + Stigli ste. + Put: udaljenost %1$s, vreme puta %2$s +\nRačunica: %3$.1f sek %4$d puta, %5$d sličica) + Izračunati put je prazan. + Izračunavanja puta nije uspelo. + Izračunavanja puta nije uspelo. + GPX fajl ne može da se sačuva. + Tačke od interesa + Učitavanje gradova… + Učitavanje ulica… + Učitavanje poštanskih brojeva… + Učitavanje ulica/zgrada… + Pretvaranje lokalnih/engleskih imena… + nije uspela + Provera ovlašćenja nije uspela + Učitavam tačke od interesa… + Predajem čvor… + Zatvaram skup promena… + Otvaram skup promena… + Radno vreme + Navođenje + Prvo odaberite odredište + Pokreni navođenje + Prikaži put + Zameni + Nema Omiljenih tačaka + Izmenjena omiljena tačka + Skidanje spiska regiona sa sajta https://osmand.net nije uspelo. + Preuzimam spisak dostupnih regiona… + Adresa + Lokalni podaci za {0} već postoje ({1}). Ažurirati ({2})\? + Preuzmi {0} - {1}\? + Fascikla sa skladištem na memorijskoj kartici nije pristupačna! + Usluga navođenja + Mrežna ili vanmrežna usluga navođenja. + Ukupna udaljenost %1$s, vreme putovanja %2$d h %3$d min. + Ne gledaj karea sa mreže na uvećanju preko ovoga. + Maksimalni zum na mreži + Mrežna pretraga + Vanmrežna pretraga + Mrežna pretraga: kućni broj, ulica, grad + Mrežna pretraga pomoću Imenika OSM-a + Tražim adresu… + Pretražujem… + Ništa nije pronađeno + Otpremam… + Otpremam podatke… + Omiljeno + Zgrada: {0}, {1}, {2} + Raskrsnica: {0} x {1} u {2} + Ulica: {0}, {1} + Grad: {0} + Ažuriraj lokalne podatke preko interneta\? + Ažuriraj tačke od interesa + Ažuriraćete tačke od interesa ako još zumirate + Nema tačaka od interesa bez interneta za ovo područje + Učitavanje podataka sa servera neuspešno. + Ne mogu da ažuriram lokalni spisak tačaka od interesa. + Podaci tačaka od interesa ažuriranu ({0}je učitano) + OsmAnd aplikacija za navođenje + Prikaži stanice prevoza + Prikaži stanice javnog prevoza na karti. + Prevoz + Dužina maršrute + stanica do izlaza + naredna udaljenost + prethodna udaljenost + Izaberite stajalište za izlaz + Završi pretragu + Prethodna maršruta + Sledeća maršruta + stanice + Stanica + Putevi + Dodaj novo pravilo + Ne mogu da izmenim format radnog vremena. + Portret, pejzaž ili kao i uređaj. + Skrenite oštro desno i nastavite + Skrenite desno i nastavite + Otpakivanje fajlova… + Memorijska kartica može samo da se čita. +\nSad je moguće samo gledati več učitane karte, ne možete da skidate nove oblasti. + Memorijska kartica nije dostupna. +\nNećete moći da vidite karte ili da pretražujete stvari. + Odabrani paket glasovnog navođenja nije dostupan + Željeni glasovni podaci su oštećeni + Nepodržava verzija glasovnih podataka + Inicijalizacija glasovnih podataka… + Zvuk isključen + Zvuk uključen + Filter + Prikaži filter + Sakrij filter + Servisu za navođenje iz pozadine je potrebno da provajder za položaj bude uključen. + Drži OsmAnd pokrenutim u pozadini + Prati Vaš položaj dok je ekran isključen. + Provajder položaja + Način nalaženja položaja koji koristi pozadinski servis: + Interval buđenja pozadinskog servisa: + Maksimalno čekanje na popravku + Postavlja najduže dozvoljeno vreme čekanja za popravljanje položaja u pozadini. + Upotreba trekbola + Koristite trekbol da pomerate kartu. + Slojevi karte + Izvor karte… + Yandex saobraćaj + Hvala Yandex-u za informacije o saobraćaju. + Omiljene tačke podeljene preko OsmAnd-a + Nema Omiljenih tačaka za snimanje + Omiljene tačke sačuvane u {0} + GPX fajl sa Omiljenim tačkama nije nađen u {0} + Sačuvaj podatke kao GPX fajl ili uvezi prolazne tačke u „Omiljene tačke“\? + Omiljene tačke uvezene + U smeru kretanja + Pravac kompasa + Pretraži stanice prevoza + Vektorske karte van mreže + Glasovna navođenja + Poništi pretragu prevoza + Rezultati prevoza ({0} do odredišta): + Rezultati prevoza (bez odredišta): + Pretraži javni prevoz + Ne rasteži (i ne čini mutnim) sličice karte na ekranima sa velikom gustinom. + Prikaži telefone u tačkama od interesa + Prikaži veb sajtove u tačkama od interesa + Izaberite izgled prikaza + Iscrtavaj neprekidno umesto crtanje slike po slike. + Neprekidno iscrtavanje + Ova karta ne može da se preuzme + Najveći zum za učitavanje unapred + Preuzmi kartu + Na nivou uvećanja {0} treba skinuti {1} sličica ({2} MB) + Uključite da računate najbrži put ili isključite da računate put sa najvećom uštednjom goriva. + Najbrži put + Preuzeto + {0} stavka(e) izabrana + Preuzmi {0} fajl(ova) ({1} MB)\? + Dnevni/noćni režim rada + Prilagodite prelazak između dnevnog i noćnog prikaza. + Po senzoru svetla + Izlazak/zalazak sunca + Noć + Dan + Odaberite paket glasovnog navođenja + Nema dostupnog glasovnog navođenja, idite u „Postavke“ → „Postavke navođenja“ → „Glasovno navođenje“ i odaberite ili skinite paket sa glasovima. + Aplikacija za stanje GPS-a nije instalirana. Da je potražite u prodavnici\? + Odaberite OsmAnd verziju za instaliranje + Učitavam OsmAnd verzije… + Preuzimanje spiska OsmAnd verzija nije uspelo + Instalirati OsmAnd - {0} od {1} {2} MB \? + Preuzimam verziju… + Verzija {0} instalirana ({1}). + Prethodna OsmAnd verzija je već instalirana. Svi postojeći podaci će biti podržani i u novoj verziji. Potrebno je ipak da izvezete Omiljene tačke iz stare aplikacije i uvezete naknadno u novoj verziji. + Ne mogu da nađen zadatu fasciklu. + OsmAnd navođenje van mreže je eksperimentalna funkcionalnost i ne radi za udaljenosti preko 20 km. +\n +\nNavođenje privremeno prebačeno na mrežni CloudMade servis. + Verzija indeksa „{0}“ nije podržana + Indeks „{0}“ ne može da stane u memoriju + Čitam keširane sličice… + NameFinder na mreži + Prilagođeni filter + Najbliže tačke od interesa + Stari format podataka karte \'\'{0}\'\' nije podržan + m + km/h + km + Indeksiram prevoz… + Indeksiram tačke od interesa… + Indeksiram kartu… + Indeksiram adrese… + Prevoz + Turizam + Prehrana + Sport + Trgovina + Ostalo + Kancelarija + Priroda + Vojska + Građevine + Slobodno vreme + Zemlja u upotrebi + Istorijsko mesto + Zdravstvo + Geokeš + Finansije + Zabava + Hitna pomoć + Obrazovanje + Prepreka + Administracija + Dodaj prolaznu tačku na snimljenu GPX putanju + GPX prolazna tačka „{0}“ dodata + Podeli položaj + Pošalji položaj + Da vidite položaj, pratite vezu ka veb lokaciji %1$s ili Android intent vezu %2$s + Lokacija: %1$s +\n%2$s + Podeli položaj koristeći + mi + mph + ft + yd + Kilometri/metri + Milje/jardi + Milje/stope + Jedinice dužine + Promeni jedinice za dužinu. + nepotpun + Jezik prikaza + Jezik za prikaz u aplikaciji (koristiće se od kada se OsmAnd ponovo pokrene). + Sistem + Pretraži po geografskom položaju + Ne mogu da rasčlanim geo nameru ’%s’. + Pretraga van mreže ne može da se obavi. + Internet OSM klasifikacija karti sa slikama. + Minimalni nivo uvećanja za vektorske karte + Koristi rasterske karte za sve preko ovog nivoa. + Instaliraj još… + Bez interneta je nemoguće izvršavanje ove operacije. + Izaberite (sličice) karte za instalaciju ili ažuriranje. + Karta već instalirana, „Podešavanja“ će se ažurirati. + Izaberite kartu sloja iznad + Karta sloja iznad + Karta sloja iznad… + Izaberite kartu sloja ispod + Karta ispod + Karta ispod… + Providnost osnovne karte + Podesite providnost osnovne karte. + Providnost sloja iznad + Podesite providnost gornjeg sloja. + Aplikacija ne može da skine sloj sa kartama %1$s, ponovna instalacija aplikacije možda pomogne. + Zvuk multimedije/navođenja + Odaberite govornika za glasovno navođenje. + Postojeće vektorske karte za ovaj položaj. +\n\t +\n\tDa biste ih koristili, idite na \'Meni\' → \'Podesi kartu\' → \'Izvor karte…\' → \'Vektorske karte van mreže\'. + Postavi duž cele putanje + Koristi trenutno odredište + Odabrani jezik nije podržan od strane instalirane Androidove sinteze teksta u govor, biće korišćen već podešeni jezik. Potražite drugi sintetizator teksta u govor u prodavnici\? + Treba da %1$s %2$s stavku(e). Nastaviti\? + %1$d of %2$d stavke(i) aktivirano. + Brze radnje + Nedozvoljen znak u imenu datoteke + Izveštaj za + Broj izmena + Broj saradnika + Mesec i država: + Noć + Ujutro + Sedmično + Dnevno + Na svakih sat vremena + Poslednja izmena karte: %s + Ažuriraj veličinu + Ažuriraj vreme + Poslednje ažuriranje: %s + OsmAnd nema dozvole da koristi memorijsku karticu + Ažuriraj sada + Uživo ažuriranje + Skidaj samo na bežičnoj mreži + Postoji izbor da li želite da kontrolišete aplikaciju preko menija ili preko komandne table. Vaš izbor uvek možete promeniti u podešavanjima komandne table. + Komandna tabla ili meniji + Koristi meni + Koristi komandnu tablu + Promeni OSM izmenu + Da li stvarno želite da sačuvate tačku od interesa bez tipa\? + Ime sadrži previše velikih slova. Ipak nastaviti\? + Legenda na karti + Kontakt + Povratne informacije + Verzije + Tehnički članci + Instalacija i otklanjanje problema + Pretraga na karti + Pregled karte + Često postavljana pitanja + ČPP + Planiranje puta + Podesite navođenje. + Kako da preuzmete karte i postavite osnovne stvari. + Prvo korišćenje + Dodaci + Ostalo + Pomozite da poboljšamo OsmAnd + Funkcionalnosti + Prvi koraci na OsmAnd aplikaciji + Nova verzija + Unesite ime države + Izaberite gde želite skladištiti karte i ostale podatke. + Prikazuje broj preostalih besplatnih preuzimanja. + Iskorišćenih besplatnih preuzimanja + Ažuriraj sve (%1$s MB) + %.1f MB + %1$.1f od %2$.1f MB + Karta koja sadrži samo puteve nije potrebna, pošto imate standardnu (celu) kartu. Svejedno je skinuti\? + Skidanja + Puna verzija + Kasnije + Uključite \'SRTM\' dodatak + Uključite dodatak \'Pomorske oznake\' + Kupi + Prikazuj baner besplatne verzije i u plaćenoj verziji. + Prikaži baner besplatne verzije + Preuzimam - %1$d fajl + Putevi + %1$s preostalih skidanja + Sve nesačuvane izmene će biti izgubljene. Da nastavim\? + Da li ste sigurni\? + Broj linija + Tačka od interesa će biti obrisana kada otpremite Vaše izmene + Sačuvano sad u: %1$s + Omiljene + Skorašnja mesta + Radni dani + Naznačite tip tačke od interesa. + Broj redova na tabli %1$s + %1$d of %2$d stavke(i) izbrisano. + %1$d od %2$d stavke(i) deaktivirano. + Izdat + Skinite podatke (\'van mreže\') da koristiti karte van mreže. + Ne, hvala + Ostalo + Kuća + Da li ste sigurni da želite da obrišete %1$d Omiljenih tačaka i %2$d Omiljenih grupa\? + Prikazuj neke detalje vektorskih karti (putevi itd.) već na manjim zumovima. + Put sačuvan kao \'%1$s\'. + Poslati GPX fajl na OSM\? + Preuzmite vektorske karte za ovaj položaj iz „Podešavanja“ („Upravljanje fajlovima karata“) ili se prebacite na dodatak „Mrežne karte“. + Testiraj glasovno navođenje + Ugrađeno iscrtavanje + Odaberite glas i pustite obaveštenje da ga testirate: + Vektorske karte se uglavnom brže prikazuju. To ne znači da će raditi dobro na svim uređajima. + Dodaci + Dodaci aktiviraju napredna podešavanja i dodatne funkcionalnosti. + Dodaci + Odaberite izvore mrežnih ili keširanih sličica karata. + Koristi mrežne karte (skidaj i keširaj sličice na memorijsku karticu). + Mrežne i karte sa sličicama + Omogućite dodatak za „Mrežne karte“ da odaberete različite izvore karata + Skidajte i upravljajte kartama uskladištenim na Vašem uređaju. + Standardne karte (vektorske) + Mrežne i keširene karte sa sličicama + Osnovna karta potrebna da obezbedi osnovne funkcionalnosti čeka u redu na preuzimanje. + Hvala Vam što koristite OsmAnd. Skinite regionalne podatke preko \'Podešavanja\' → \'Upravljanje fajlovima karata\' da i bez interneta vidite karte, nađete adrese, tačke od interesa, javni prevoz i još mnogo toga. + Otkaži preuzimanje\? + Postavi transparentnost (0 - skroz transparentno, 255 - skroz neprovidno) + Položaj još nije poznat. + Polazna tačkajoš nije određena. + Tapnite na postojeću stavku da vidite više detalja, dugo držite da je deaktivirate ili obrišete. Trenutno podataka na uređaju (%1$s slobodno): + Za zemlje u kojima se vozi levom stranom. + Vožnja levom trakom + OsmAnd navigacija van mreže je privremeno nedostupna. + Način za pretragu Omiljenih tačaka + Pretraga javnog prevoza + Koordinate + Pretraga adresa + Pretraga POI (tačaka od interesa) + Format + Podaci u rutiranju + Prikaži više detalja na karti + Povećaj nivo detalja na karti. + Prikaži izohipse + Prikaz od kog nivoa uvećanja (zahteva podatke o izohipsama): + Optimizuj kartu za + Način iscrtavanja + Poligone + Načini providnim sve vazdušne odlike terena. + Držite desno i nastavite + Držite levo i nastavite + Kružni tok: Izađite na %1$d. izlazu i nastavite + Put podeljen preko OsmAnd-a + Podelite put kao GPX fajl + Što pre + Privatni + Može da se koristi za praćenje + Može da se koristi za identifikaciju + Javni + Izbriši oznaku parkinga + Označi kao poziciju parkinga + Mesto za parkiranje + AM + PM + Uzeti vozilo sa parkinga u: + Položaj parkiranja Vašeg vozila. %1$s + Parking bez vremenskog ograničenja + Parking sa vremenskim ograničenjem + Dodajte obaveštenje u aplikaciju kalendara + Vremenski neograničeno + Vremenski ograničeno + Izaberite vrstu parkiranja + Izbriši oznaku parkinga + Obrisati oznaku položaja parkinga\? + Odaberite maksimalno vreme parkiranja + Obaveštenje da preuzmete kola je dodato u Vaš kalendar i tamo možete da ga izmenite ili uklonite. + Upozorenje + Uzmite kola sa parkinga + Deljeni položaj + Tačka polaska je previše daleko od najbližeg puta. + Ukupno memorije + Ukupno memorije koje je alocirala aplikacija %1$s MB (Dalvik %2$s MB, ostalo %3$s MB). +\nProporcionalno memorije %4$s MB (Android limit %5$s MB, Dalvik %6$s MB). + Iskorišćena memorija + Zauzeta memorija %1$s MB (Android limit %2$s MB, Dalvik %3$s MB). + Brzina simulacije puta: + Kola su parkirana na + Minuta + Časova + Čekam nalaženje položaja za izračunavanje puta + Nastaviti prethodno nezavršeno navođenje\? (%1$s sekundi) + Bez naplate putarina + Upozorenja o saobraćaju + Radari + Podesite interval buđenja: + Mini karta puta + Vreme pristizanja + Parking + Pritisnite ikonicu za zaključavanje da otključate + Neprekidno + Zaustavi +\n rad u pozadini + Prozirne spravice + Pravac pregleda + Tip prevoza: + Način prevoza: + Napravi filter tačaka od interesa + Približna pokrivenost karte i kvalitet: +\n • Zapadna Evropa: **** +\n • Istočna Evropa: *** +\n • Rusija: *** +\n • Severna Amerika: *** +\n • Južna Amerika: ** +\n • Azija: ** +\n • Japan i Koreja: *** +\n • Bliski istok: ** +\n • Afrika: ** +\n • Antarktik: * +\n Većina država na svetu se može skinuti +\n Od Avganistana do Šri Lanke, gore od Aljaske do Australije. Argentina, Brazil, Kanada, Francuska, Nemačka, Meksiko, Velika Britanija, Španija, … +\n + Doprinesite direktno OSM-u +\n • Prijavljujte greške u podacima +\n • Otpremajte GPX putanje direktno iz aplikacije +\n • Dodajte tačke od interesa i otpremajte ih direktno na OSM (ili kasnije ako niste na mreži) +\n • Opciono snimanje puta i u pozadini (dok je uređaj u režimu spavanja) +\n OsmAnd je softver otvorenog koda koji se aktivno razvija. Svako može da doprinese aplikaciji prijavljujući greške, poboljšavajući prevode ili razvijajući nove funkcionalnosti. Dodatno se projekat oslanja i na novčanu pomoć da se finansira programiranje i testiranje novih funkcionalnosti. +\n + Funkcionalnosti za bicikliste i pešake +\n • Pregled staza za pešanjenje, hodanje i bicikle, sjajno za aktivnosti van kuće +\n • Posebno traženje puta i režimi izgleda za bicikle i pešake +\n • Opcione stanice javnog prevoza (autobus, tramvaj, voz) uključujući imena linija +\n • Opciono snimanje puta u lokalni GPX fajl ili mrežni servis +\n • Opcioni prikaz brzine i nadmorske visine +\n • Prikaz konturnih linija i visinskih obrisa (preko dodatnog dodatka) + Bezbedonosne funkcionalnosti +\n • Opciono automatsko prekazivanje dnevnog/noćnog režima +\n • Opcioni prikaz ograničenja brzine, sa podsetnikom ako je prekoračite +\n • Opciono zumiranje puta relativno na brzinu kretanja +\n • Deljenje Vašeg položaja da prijatelji mogu da Vas nađu +\n + Korišćenje OSM i podataka sa Vikipedije +\n • Visokokvalitetne informacije sa najboljih projekata sa zajedničkim radom +\n • OSM podaci dostupni po državi ili regiji +\n • Vikipedija tačke od interesan, odlično za turističko razgledanje +\n • Neograničeno besplatnih skidanja, direktno iz aplikacije +\n • Kompaktne vektorske karte van mreže koje se ažuriraju bar jednom mesečno +\n +\n • Izabir između celokupnih podataka regije ili samo mreže puteva (primer: ceo Japan je 700MB, a samo putevi su 200MB) + Pregled karte +\n • Prikazuje Vaš položaj i pravac +\n • Opciono poravnjavanje slike prema kompasu ili smeru kretanja +\n • Čuvanje najbitnijih mesta u Omiljenim mestima +\n • Prikaz tačaka od interesa oko Vas +\n • Prikazuje specijalizovane sličice sa mreže, satelitske snimke (sa Binga), različite preklapajuće slojeve za obilaske/navođenje sa GPX stazama i dodatne slojeve sa podesivom providnošću +\n • Opcioni prikazujete imena na karti: na engleskom, lokalnom jeziku ili fonetski izgovor +\n + Navođenje +\n • Radi preko interneta (brzo) ili bez interneta (ne plaćate roming kada ste van zemlje) +\n • Glasovno navođenje od skretanja do skretanja (snimljeni ili sintetizovani glasovi) +\n • Opciono navođenje u odgovarajuću traku, prikaz imena ulica i procenjeno vreme dolaska +\n • Podrška za usputne tačke na putu +\n • Automatsko preračunavanje puta kada se odaljite sa putanje +\n • Pretraga mesta po adresi, tipu (npr. restorani, hoteli, benzinske pumpe, muzeji), ili po koordinatama +\n + OsmAnd+ (OSM Automated Navigation Directions) je aplikacija za karte i navođenje koja pristupa besplatnim visokokvalitetnim OSM podacima iz celog sveta. +\n Uživajte u optičkom i glasovnom navođenju, pregledu tačaka od interesa, pravljenjem i upravljanjem GPX putanjama, korišćenjem izobata i podacima o visini, izboru između režima vožnje, biciklizma, pešačenja, izmena na OSM-u i još mnogo toga. +\n +\n OsmAnd+ je verzija aplikacije koja se plaća. Njenom kupovinom, podržavate projekat, finansirate razvoj novih funkcionalnosti i primate najnovija ažuriranja. +\n +\n Neke od glavnih karakteristika: + OsmAnd+ (OSM Automated Navigation Directions) +\n +\n OsmAnd+ je is aplikacija otvorenog koda za navođenje sa pristupom raznim globalnim podacima OSM-a. Svi podaci na karti (vektorski ili sličice karte) se mogu uskladištiti na memorijsku karticu telefona za upotrebu i bez interneta. Nude se mogućnosti navođenja i sa i bez internet konekcije, uključujući u glasovno navođenje. +\n +\n OsmAnd+ je verzija aplikacije koja se plaća, a njenom kupovinom, podržavate projekat, finansirate razvoj novih funkcionalnosti i primate najnovija ažuriranja. +\n +\n Neke od osnovnih mogućnosti: +\n - Kompletna funkcionalnost rada van mreže (skladištenje skinutih vektorskih karti i sličica na skladište telefona) +\n - Dostupne su celokupne vektorske karte celog sveta za rad van mreže +\n - Neograničena preuzimanja država ili regiona direktno iz aplikacije +\n - Funkcionalnost Vikipedije van mreže (preuzimanje Vikipedijih tačaka od interesa), sjajno za turistička razgledanja +\n - Mogućnost dodavanja različitih dodatnih slojeva, kao što su GPX sloj ili staze za navođenje. Tačke od interesa, Omiljene tačke, konturne linije, stanice javnog prevoza, dodatne mape sa podesivom providnošću +\n +\n - Pretraga van mreže za adrese i tačke od interesa +\n - Rutiranje bez potrebe za internetom za udaljenosti srednjeg dometa +\n - Režimi rada za automobil, biciklu i pešake sa opcionim: +\n - Automatskim prebacivanjem dnevnog/noćnog režima rada +\n - Zumiranjem relativnim na brzinu +\n - Okretanjem mape prema kompasu ili pravcu kretanja +\n - Navođenjem u pravu traku, prikaz ograničenja brzine, snimljeni i sintetizovani glasovi za navođenje +\n + Globalni pregled karti na mobilnim uređajima & navigacija za OSM karte na mreži i van mreže + OsmAnd+ karte & navođenje + OsmAnd je softver otvorenog koda na kome se aktivno radi. Svako može da doprinese aplikacija tako što će da prijavljuje greške, poboljšava prevode ili da programira nove funkcionalnosti. Dodatno, projekat se oslanja na novčane priloge da bi se finansirao razvoj i testiranje novih funkcija. +\n +\n Približna pokrivenost karte i kvalitet: +\n • Zapadna Evropa: **** +\n • Istočna Evropa: *** +\n • Rusija: *** +\n • Severna Amerika: *** +\n • Južna Amerika: ** +\n • Azija: ** +\n • Japan i Koreja: *** +\n • Bliski istok: ** +\n • Afrika: ** +\n • Antarktik: * +\n Većina država na svetu se može skinuti! +\n Dobijte pouzdanog navigatora u Vašoj državi - bilo da je to Francuska, Nemačka, Meksiko, Velika Britanija, Holandija, SAD, Rusija, Brazil ili neka druga. + Doprinesite OSM-u +\n • Prijavljujte greške u podacima +\n • Otpremajte GPX putanje direktno iz aplikacije +\n • Dodajte tačke od interesa i otpremajte ih direktno na OSM (ili kasnije ako niste na mreži) +\n + Šetanje, planinarenje, gradske ture +\n • Karta Vam pokazuje staze za pešačenje i planinarenje +\n • Vikipedija na Vašem jeziku Vam može reći mnogo za vreme tura po gradu +\n • Stanice javnog prevoza (autobusi, tramvaji, vozovi), uključujući i imena linija će Vam pomoći kretanje po nepoznatom gradu +\n • GPS navođenje u režimu pešačenja Vam pravi putanju koristeći puteve na kojima se može proći peške +\n • Otpremajte i pratite GPX putanje ili snimajte i delite Vaše sopstvene +\n + Bickle +\n • Nađite staze za bicikle na kartama +\n • GPS navigacija u režimu za bicikle Vam pronalazi putanju na putevima za bicikle +\n • Vidite Vašu brzinu i nadmorsku visinu +\n • Opcija GPX snimanja Vam omogućava da snimiti Vaše putovanje i da ga delite +\n • Uz još dodataka možete da uključite i izohipse i reljef + Skijanje +\n OsmAnd dodatak za skijaške mape Vam omogućava da vidite staze za skijanje uz njihovu težinu i dodatne informacije, kao što su položaj ski liftova i ostalih objekata. + Karta +\n • Prikaz tačaka od interesa oko Vas +\n • Podešavanje karte prema pravcu kretanja (ili kompasu) +\n • Prikazuje Vaš položaj i pravac u kom je telefon usmeren +\n • Delite položaj da prijatelji mogu da Vas nađu +\n • Držite najbitnija mesta u „Omiljenim“ +\n • Dozvoljava da izaberete kako prikazujete imena na karti: na engleskom, lokalnom jeziku ili fonetski izgovor +\n • Prikazuje specijalizovane sličice sa mreže, satelitske snimke (sa Binga), različite preklapajuće slojeve za obilaske/navođenje sa GPX stazama i dodatne slojeve sa podesivom providnošću +\n + GPS navođenje +\n • Odabir između navođenja preko interneta (brzo) ili bez interneta (ne plaćate roming kada ste van zemlje) +\n • Glasovno navođenje od skretanja do skretanja (snimljeni ili sintetizovani glasovi) +\n • Automatsko preračunavanje puta kada se odaljite sa putanje +\n • Opciono navođenje u odgovarajuću traku, prikaz imena ulica i procenjeno vreme dolaska +\n • Automatsko menjanje režima dana/noći za bezbednije putovanje +\n • Prikaz ograničenja brzine i podsetnik kada je prekoračite +\n • Zumiranje karte se prilagođava brzini +\n • Pretraga mesta po adresi, tipu (npr. restorani, hoteli, benzinske pumpe, muzeji), ili po koordinatama +\n • Podrška za usputne tačke na putu +\n • Snimanje Vaših, ili otpremanje GPX staza i njihovo praćenje +\n + OsmAnd (OSM Automated Navigation Directions) je aplikacija za karte i navođenje koja pristupa besplatnim visokokvalitetnim OSM podacima iz celog sveta. +\n +\n Uživajte u optičkom i glasovnom navođenju, pregledu tačaka od interesa, pravljenjem i upravljanjem GPX putanjama, korišćenjem izobata i podacima o visini (kroz dodatak), izboru između režima vožnje, biciklizma, pešačenja, izmena na OSM-u i još mnogo toga. + OsmAnd (OSM Automated Navigation Directions) +\n +\n OsmAnd je is aplikacija otvorenog koda za navođenje sa pristupom raznim globalnim podacima OSM-a. Svi podaci na karti (vektorski ili sličice karte) se mogu uskladištiti na memorijsku karticu telefona za upotrebu i bez interneta. Nude se mogućnosti navođenja i sa i bez internet konekcije, uključujući u glasovno navođenje. +\n +\n Neke od osnovnih mogućnosti: +\n - Kompletna funkcionalnost rada van mreže (skladištenje skinutih vektorskih karti i sličica na skladište telefona) +\n - Dostupne su celokupne vektorske karte celog sveta za rad van mreže +\n - Preuzimanje države ili regiona direktno iz aplikacije +\n - Mogućnost dodavanja različitih dodatnih slojeva, kao što su GPX sloj ili staze za navođenje. Tačke od interesa, Omiljene tačke, konturne linije, stanice javnog prevoza, dodatne mape sa podesivom providnošću +\n - Pretraga van mreže za adrese i tačke od interesa +\n - Rutiranje bez potrebe za internetom za udaljenosti srednjeg dometa +\n - Režimi rada za automobil, biciklu i pešake sa opcionim: +\n - Automatskim prebacivanjem dnevnog/noćnog režima rada +\n - Zumiranjem relativnim na brzinu +\n - Okretanjem mape prema kompasu ili pravcu kretanja +\n - Navođenjem u pravu traku, prikaz ograničenja brzine, snimljeni i sintetizovani glasovi za navođenje +\n +\n Ograničenja besplatne verzije OsmAnd-a: +\n - Ograničen broj skidanja karti +\n - Bez pristupa tačkama od interesa sa Vikepdije kada nema interneta +\n +\n OsmAnd se aktivno razvija i naš projekat i kontinuirani napredak se oslanja na finansijske priloge za razvoj i testiranje novih funkcionalnosti. Molimo razmislite o kupovini OsmAnd+ aplikacije ili finansiranju specifičnih novih funkcionalnosti ili davanju donacije na https://osmand.net. + Globalni pregled karti na mobilnim uređajima i navigacija za OSM karte na mreži i van mreže + OsmAnd karte & navođenje + Pauziraj muziku + Glasovni upiti pauziraju puštenu muziku. + Prilepi na put + Automatsko zumiranje karte + Nivo zumiranja prema Vašoj brzini (dok je karta sinhronizovana sa Vašom pozicijom). + Bez autoputeva + Koristi kompas + Koristi kompas kada pravac nije prepoznat na drugi način. + Podesite upozorenja u saobraćaju (ograničenja brzine, forsirana zaustavljanja, ležeći policajci, tuneli), radare i podatke o traci. + Prikazuj upozorenja… + Gorivo + Za turiste + Prodavnica hrane + Pomoć na putu + Razgledanje znamenitosti + Restorani + Smeštaj + Zabava + Javni prevoz + Prva pomoć + Parking + Napredni način rada… + Dodaj oznaku + Završna tačka je predaleko od najbližeg puta. + Međuvreme + Prolazno odredište + Dodaj kao prolazno odredište + Stigli ste na prolazno odredište + Prolazno odredište %1$s je predaleko od najbližeg puta. + Odredišta + Već ste postavili odredišnu tačku: + Zameni odredište + Dodaj kao početno prolazno odredište + Dodaj kao završno prolazno odredište + Dodaj kao početno prolazno odredište + Dodaj kao poslednje prolazno odredište + Prolazno odredište %1$s + Odredište %1$s + Ukloni odredište + Nema puta + Usluge beleženja putanje + Omogućite GPS u podešavanjima + Računaj moguću približnu putanju za velike razdaljine + Omogućite dodatak za „snimanje putovanja“ radi korišćenja usluga beleženja (GPX beleženje, praćenje položaja na mreži) + Prikazuj pravac odredišta + Obrazac boja puteva + Izaberite obrazac boja puteva: + Aplikacija ZXing barkod skener nije instalirana. Pretražiti na Guglovoj prodavnici\? + Zatvori promene + Program je u bezbednom načinu rada (isključite u „Postavkama“). + Bezbedni način rada + Pokreni aplikaciju u bezbednom načinu rada (koristeći sporiji Androidov kod umesto bržeg programskog). + Samo putne karte + Odaberite kada će se prikazivati samo putne karte: + Zabrani prikaz regionalnih granica (admin nivoi 5-9). + Prikazuj puteve prema putanjama OSMC-a. + Sloj nad kartom sa simbolima za pešačenje + Prikazuj puteve prema lestvici SAC-a. + Alpska skala za planinarenje (SAC) + Karta puteva + Orijentacija ekrana + Pejzaž + Portret + Kao i uređaj + Pretraži okolinu + Pretraži blizu trenutnog središta karte + Tražim signal… + Skinite regione + Kasnije + Početak + Napravite polukružno i nastavite + Skrenite blago levo i nastavite + Skrenite oštro levo i nastavite + Skrenite levo i nastavite + Skrenite blago desno i nastavite + Uobičajena karta + Izohipse + delovi + Kliknite „Koristi položaj…“ da dodate belešku određenoj lokaciji. + Snimam %1$s %3$s %2$s + Ne mogu da pustim snimak. + Otvori program spoljnog izvođača + Izvodi se određeni zvučni zapis +\n%1$s + Snimanje zvuka/video snimaka. Zaustavite snimanje pritiskom na AV spravicu. + Koristi program kamere + Koristi sistemsku aplikaciju za slike. + Koristi sklopnog snimača videa + Koristi sistemsku aplikaciju za video snimanje. + Oblik izlaznog snimka + Izlazni video format: + Podrazumevana radnju spravice + Podrazumevana radnja spravice: + Snimi zvuk + Snimi video + Grčki + Nemački (govorni) + Nemački + Gruzijski + Francuski + Finski + Holandski + Danski + Češki + Hrvatski + Katalonski + Bugarski + Bošnjački + Beloruski (latinični) + Beloruski + Baskijski + Armenijski + Arapski + Albanski + Afrikans + Engleski (britanski) + Engleski + Portugalski (brazilski) + Kineski + Nebeska karta + Svetska karta + Svetska plaćanja bitskim novcem + Svetske pomorske oznake + Ispravka svetske geografske visine + Adresa u državi + Prevod + Razno + Glas + Uvek prikazuj položaj u sredini + Računaj put između tačaka + dana + Poveži se + Boja prikaza + Prazan GPX fajl + %1$s +\nStaza %2$s + %1$s +\nTačaka + %1$s +\nTačke putanje %2$s + Tačka %1$s + %1$s tačaka + Odsečak + Vreme kretanja: %1$s + Vremenski raspon: %1$s + Padina/uspon: %1$s + Raspon visine: %1$s + Prosečna visina: %1$s + Najveća brzina: %1$s + Prosečna brzina: %1$s + Vreme završetka: %1$tF, %1$tT + Početno vreme: %1$tF, %1$tT + Rastojanje: %1$s (%2$s tačaka) + Prolazne tačke: %1$s + Podputanje: %1$s + " +\n +\nPritisnite i držite za mogućnosti" + Automatski pokreni vodiča za skretanje + " +\n +\nPritisnite i držite radi prikaza karte" + Prolazna tačka + Trenutno vreme + Učitavam %1$s… + Nazivi tačaka od interesa na sloju iznad + Postavite odredište + Sačuvaj kao skup Omiljenih + Prikazuj dugmad za uveličavanje + Prikazuj dugmad za uveličavanje prilikom navođenja. + Raspored po imenu + Raspored po daljini + Sortiraj po kategoriji + Vreme razdvajanja + Odaberite stazu + Odaberite GPX fajl dugim pritiskanjem. + Nema ničeg pronađenog. Ako program ne može naći ovu oblast, možete to učiniti sami (pogledajte https://osmand.net). + Nema pronađenih preuzimanja, proverite vezu sa Internetom. + Prikazuj biciklističke staze + Prikazuj materijale puteva + Prikazuj stanje puteva + Prikazuj zabrane pristupa i putarine + Zabrane pristupa + Manje detalja + Više detalja + Prikaz zgrada je na uveličanju 15 + Šume i šumarke + Tekst + Puteve koji nisu za motorna vozila + Zgrade + Pokreni novi odsečak + Zaustavi praćenje na mreži + Započni praćenje na mreži + Praćenje na mreži (potreban GPX) + Ako je GPX beleženje uključeno, šalje podatke o putanji odabranom veb servisu. + Komandna tabla + Napred + Lokalna imena + Željeni jezik karte + Željeni jezik za oznake na karti (ako nije dostupan, koristiće se engleski ili lokalna imena). + Drži + Pauziraj navođenje + Nastavi navođenje + Oboji zgrade po tipu + Pešice + Bicikl + Kola + Pregledaj kartu + Da preuzmem nedostajuće karte %1$s (%2$d MB)\? + Prolazne tačke + Sve + Ture + Min/Maks + Maks/Min + Vreme u pokretu + Uspon/Spust + %1$d od %2$d + Prosek + Rastojanje + Trajanje + h + Podesite kako snimati Vaša putovanja. + Snimanje putovanja + Odaberite broj porta Vašeg proksija (npr. 8118). + Proksi port + Navedite ime domaćina Vašeg proksija (npr. 127.0.0.1). + Proksi domaćin + Podesite HTTP proksi za sve mrežne zahteve. + Omogući HTTP proksi + Glasovno navođenje + Izaberite glasovno navođenje. + Navedite interval logovanja snimanja putanje prilikom navođenja + Interval beleženja za vreme navođenja + Interval beleženja + GPX putanja se automatski čuva u fasciklu sa putanjama dok je navođenje uključeno. + Automatski snimaj putanju za vreme navođenja + Sačuvaj trenutnu putanju + Sačuvaj odmah trenutnu putanju kao GPX fajl. + Glavno beleženje pozicije u GPX fajl može da se uključi i isključi korišćenjem spravice za GPX beleženje na karti. + Praćenje putanje po zahtevu + Uključi da GPS radi u pozadini + Međuvreme buđenja GPS-a + Međuvreme opšte prijave + Uvek pitaj + Zaustavi + Prekinuti pozadinski režim GPS-a\? + Pozadinske usluge OsmAnd-a su i dalje pokrenute. Prekinuti i njih\? + Upozorenja o gužvama u saobraćaju + Obližnje zanimljive tačke + Omiljeno u blizini + Upozorenje saobraćajne gužve + Brzinska kamera + Smirivanje saobraćaja + Naplata putarine + Upravljanje granicama + Štampaj putanju + Prevoz + Putevi + Autobuski putevi + Putevi trolejbusa + Putevi deljenog taksija + Tramvajski putevi + Železnički putevi + Izbegavaj puteve… + Tramvajski i železnički putevi + Autobus, trolejbus, šatl bus + Ispravan kôd lokacije +\nPredstavlja površinu: %1$s x %2$s + Prekratak kôd lokacije +\nNavedite puni kôd + Neispravni kôd lokacije +\n + Otvoreni kôd lokacije + MGRS + Američki atlas puteva + Fajl sa podacima tačaka od interesa \'%1$s\' je nepotreban i može biti izbrisan. + Preuzmi podatke za pretragu tačaka od interesa bez interneta. + Nađeno nekoliko kategorija tačaka od interesa. + Grad + Zaseok + Obriši %1$s\? + Postavke izvora karte + Sloj iznad / ispod + Opcije specifične za vektorski renderer + Automatsko centriranje samo za vreme navođenja + Ponovi instrukcije navođenja + Ponavljaj instrukcije navođenja u pravilnim razmacima. + Samo ručno (klikom na strelicu) + Vreme dok se pregled karte ne sinhronizuje sa trenutnom pozicijom. + Prozirna tema + Nema dovoljno slobodnog prostora da se skine %1$s MB (slobodno: %2$s). + OsmAnd radi u pozadini dok je ekran isključen. + Pozadinski režim + Potrebno za podnošenje na openstreetmap.org. + Vaše OSM korisničko ime + Opšte postavke aplikacije + Podesite prikaz i opšte postavke aplikacije. + Opšte + Upravljanje fajlovima karata + Opšte postavke + Odaberite opcije navođenja. + Navođenje + Postavke specifične za profil + Fajl sa već izvezenim Omiljenim tačkama već postoji. Zameniti ga\? + Korisnički određeno + Vikipedija (van mreže) + Glasovna navođenja (TTS, preferirana) + Glasovna navođenja (snimljena, ograničene funkcionalnosti) + Svetske tačke od interesa sa Vikipedije + Svetske i tematske karte + Antarktik + Australija i Okeanija + Azija + Afrika + Rusija + Evropa - Nemačka + Evropa - Francuska + Evropa + Južna Amerika + Centralna Amerika + Severna Amerika - Sjedinjene Američke Države + Evropa - Holandija + Severna Amerika + Prikazuj opis tačaka od interesa. + Besplatna verzija + Možete skinuti ili ažurirati %1$s karti. + Prikaži trenutnu putanju + Zapisujte putanju koristeći GPX spravice ili preko podešavanja za \"Snimanje putovanja\". + Odredite vremenski bafer u kojem se drže lokacije da se pošalju dok nema mreže + Vremenski bafer za onlajn praćenje + Veb adresa za onlajn praćenje + Unesite veb adresu sa sledećom sintaksom za parametre: lat={0}, lon={1}, timestamp={2}, hdop={3}, altitude={4}, speed={5}, bearing={6}. + Interval za onlajn praćenje + Odredite interval praćenja dok ste na mreži. + Prikazuj i upravljaj OSM tačkama od interesa/beleškama iz baze podataka sa uređaja. + Tačke od interesa/beleške sa OSM-a sačuvane na uređaju + Asinhrono OSM uređivanje: + Izbriši izmenu + Otpremi izmene u OSM + Otpremi sve + {0} tačaka od interesa/beleški otpremljeno + Otpremam… + Izmene tačaka od interesa unutar aplikacija ne utiče na skinute fajlove karata. Izmene su snimljene u lokalni fajl na uređaju. + Ako je omogućeno uređivanje van mreže, tada će se promene prvo sačuvati lokalno i otpremiti po zahtevu, u suprotnom će se promene odmah otpremiti. + Uređivanje van mreže + Koristi fluorescentne boje za prikaz putanja i puteva. + Fluorescentni sloj iznad karte + Nedovoljno memorije da se prikaže odabrano područje + Koliko brzo želite da dobijete obaveštenje o pristizanju\? + Obaveštenje o dolasku + U poslednjih par metara + Kasno + Normalno + Rano + Opcije vezane za pristupačnost. + Koristite trekbol za zumiranje + Promenite zumiranje vodoravnim pokretima trekbola. + Ja sam ovde + Zaustavi automatsko najavljivanje + Započni automatsko najavljivanje + Izaberite način izražavanja odnosnih pravaca prilikom kretanja + Stil smera + U smeru kazaljke na satu (12 sektora) + Bočno (8 sektora) + Nema informacija + Nadmorska visina + Tačnost + prema + sati + levo-napred + na levo + levo-nazad + nazad + desno-nazad + na desno + desno-napred + napred + sever-severozapad + severozapad + zapad-severozapad + zapad + zapad-jugozapad + jugozapad + jug-jugozapad + jug + jug-jugoistok + jugoistok + istok-jugoistok + istok + istok-severoistok + severoistok + sever-severoistok + sever + Nivo zuma je + Zumiraj + Odzumiraj + Nazad na meni + Prema Android sistemskim postavkama + Uključite funkcionalnosti za bolju pristupačnost. + Režim pristupačnosti + Vrati se na položaj + Informacije + Prikaži lenjir + Donirajte da biste videli nove funkcionalnosti u aplikaciji. + Podržite nove funkcionalnosti + Podrška + Da biste otpremili GPX fajlove morate da navedete OSM korisničko ime i lozinku. + Opis + Oznake + Vidljivost + Putevi sa velikim kontrastom + Nemački atlas puteva + Podrazumevano + Stil puteva + Mesta za parkiranje + Ravnanje staze + Skijališta + prethodni dani + Razvoj OsmAnd-a + Podešavanja za razvoj i otklanjanje grrešaka kao što su simulacija navođenja, performanse iscrtavanja ili glasovni upiti. Namenjeno programerima, nepotrebno prilikom normalnog korišćenja. + Doprinosite OSM-u, preko pravljenja i izmena OSM objekata tačaka od interesa, otvaranja ili komentarisanja OSM beleški, i prilaganjem snimljenih GPX fajlova iz OsmAnd-a tako što date Vaše korisničko ime i lozinku. OpenStreetMap.org je globalni projekat mapiranja sveta u javnom domenu, vođen zajednicom. + OpenStreetMap uređivanje + Čini da se funkcije pristupačnosti iz uređaja mogu koristiti direktno u OsmAnd-u. Može da upravlja brzinom izgovora sintetizovanih glasova, podešavanje D-pada za navođenje, korišćenje trekbola za kontrolu zumiranja, izgovara tekst, npr. da Vam kaže položaj. + Pristupačnost + Karta je preuzeta + QR kod + Osnovna karta sveta (koja pokriva celu Zemlju pri pogledu izdaleka) ne postoji ili je zastarela. Preuzmite osnovnu kartu sveta za upotpunjavanje radne sredine. + OSM tačka od interesa je napravljena + Otpremi + Ažuriraj + Predložene objekte + Šta je novo + Šta je novo + Pročitaj više + Albanski (toskijski) + Zapadnofrizijski + Makedonski + Niskonemački + OSM izmene deljene preko OsmAnd-a + Položaj + Izmeni GPX prolaznu tačku + Izbrisati prolaznu tačku GPX-a\? + Predaj + Komentar dodat + Komentar ne može da se doda. + Ime autora + Poruka + Lozinka OSM-a (opciono) + Beleška ne može da se zatvori. + Beleška zatvorena + Ne mogu da napravim belešku. + Beleška napravljena + Zatvori belešku + Ponovo otvori belešku + Dodaj komentar + Napravi belešku + Beleška OSM-a + Napravi belešku OSM-a + Iskomentarisana beleška OSM-a + Ponovo otvorena beleška OSM-a + Otvori belešku OSM-a + Izbrisane OSM tačke od interesa + Izmenjene OSM tačke od interesa + Sačuvaj za pregled van mreže + Umnoženo je u ostavu isečaka + Prikaži pri pokretanju + Ako je isključeno, odmah se prikazuje karta. + Pristupi sa karte + Dugme za meni ne pokreće meni, nego komandnu tablu + Izaberite tačnu vrstu tačke od interesa ili je preskočite. + Izaberite bitski protok zvuka. + Bitski protok zvuka + Izaberite oblik za zvučni izlaz. + Oblik zvučnog izlaza + Izaberite svojstva prikaza videja. + Kvalitet izlaza slike + Najviša kakvoća + Najniža kakvoća + Sakrij + Blizu + Nije određena adresa + Konjske staze + Vreme: + Rastojanje: + Odobri pristup mikrofonu. + Odobri pristup kameri. + Odobri pristup podacima o položaju. + Zvuk + Izaberite koje puteve želite da izbegavate tokom navođenja. + Izaberite ili preuzmite glasovno navođenje svog jezika. + Izaberite glasovno navođenje + Dostupne karte + Nadogradnje uživo + Veličina zauzeća svih snimljenih odsečaka. + Veličina ostave + Povećaj vremensko ograničenje za snimke. + Dužina odsečka snimka + Prepisuje snimke kada se prepuni mesto za njihovo skladištenje. + Koristi seckanje snimaka + Seckanje snimanja + Nije označeno + Početna tačka + Tip + Opozovi sve + izbrisane stavke + Stavka uklonjena + Sličice tačaka od interesa + Zameni polaznu i ciljnu tačku + Označi + Put je preprečen + Dodaj vremenski razmak + Uklonite preuzete nadogradnje i vratiti se na izvornu kartu + Saznajte više + Ukloni + Nema dostupnih podataka + Podzemne objekte + Adresa e-pošte + Sačuvaj izmene + Stanje + Pronađi parkiralište + Prikazuj mnogouglove + Prikaži puteve planinskih bicikli + Preporučuje se da isključite prikaz poligona. + Oznaka karte + Oznake na karti + Broj aktivnih oznaka + Očistiti istoriju oznaka karata\? + Ukloniti sve aktivne oznake\? + Pokreni mogućnost označavanja na karti. + Obrnut redosled + Odaberi oznake na karti + Dodaj u oznake na karti + Dodati sve tačke u oznake na karti\? + Spravice + Traka pribora + Druga oznaka karte + Prva oznaka karte + Otpremi belešku OSM-a + Preuzeti {0} fajla\? +\nBiće iskorišćeno {1} MB (od {2} MB). + Preuzeti {0} fajla\? +\n {3} MB je iskorišćeno privremenog prostora, a {1} MB trajnog. (od {2} MB.) + Nema dovoljno prostora! +\n {3} MB je privremeno potrebno, a {1} MB trajno. +\n (dostupno je samo {2} MB.) + Prikazuj prozirnu traku pretrage + Otpremi bezimeno + Ostale oznake + Izaberite oznaku karte + Ova članarina omogućava nadogradnje svakog sata svih svetskih karata. +\n Deo vrednosti se vraća zajednici OSM-a i isplaćuje svakom doprinosiocu OSM-a. +\n Ako volite OsmAnd i OSM i želite da ih podržite i da oni podrže Vas, ovo je savršen način za to. + Molimo prvo kupite članarinu na OsmAnd uživo + Postavke članarine + Deo Vaših priloga će biti poslan korisnicima OSM-a koji otpremaju izmene karata te oblasti. + Hvala Vam na podršci OsmAnd-u! +\nDa aktivirate sve nove funkcionalnosti treba da ponovo pokrenete aplikaciju. + Unesite javno ime + Molimo unesite ispravnu adresu e-pošte + Neaktivno + Aktivno + Mesečno plaćanje + Cena po mesecu + Podržana oblast + Ne prikazuj moje ime u izveštajima + Javno ime + Potrebno da bi Vam obezbedili informacije o Vašim doprinosima. + Pretplati se + Članarina na OsmAnd uživo + Rangiranje OSM uređivača + %1$s izmena, u zbiru %2$s mBTC + Izmene %1$s, ugled %2$s, ukupno izmena %3$s + Broj primaoca + Prilozi + OSM korisničko ime i lozinka + Preračunaj putanju + Potpuni izveštaj + Gornja traka + Deljeno skladište + Prebačeno na internu memoriju pošto je označeno skladište za podatke zaštićeno od pisanja. Izaberite fasciklu za skladište u koju može da se piše. + Izbegavaj put + Završi navođenje + Pomeri ↓ + Pomeri ↑ + Aplikaciji je sada dozvoljen upis na spoljno skladište, ali morate ponovo da je pokrenete. + Izveštaj + Nema pronađenih prolaznih tačaka + Dodajte oznake karata na karti + Podebljano + Srednje + Tanko + Nežno + Prikaz karte + Sortiraj + Navedi gore + Prostri + Nije ugrađeno + Razgranat spisak + Prazan spisak + Prostrt spisak + Skupljeni spisak + Karta vezana za položaj + Unesite opis. + Unesite kategoriju + Unesite ime + Izaberite kategoriju + Podrazumevana boja + Najkraće vreme između najava. + Međuvreme pametnih najava + Obavesti me samo ako je pravac ka ciljnoj tačci izmenjen. + Pametna najava + Bez automatskog preračunavanja putanje ako se samo krećete u suprotnom pravcu. + Nema proračuna puta zbog pogrešnog pravca + Bez preračunavanja putanje kada se ona napusti. + Nema proračunavanja putanja po njegovom napuštanju + Usmereno vreteno + Magnetno vreteno + Dodatak za pristupačnost: Odredište nije podešeno + Navođenje uživo OSM-a + Omogući navođenje izmena OSM-a uživo. + Na zahtev\? + Razmislite o plaćanju dodatka za „izohipse“ radi davanja podrške daljem razvoju. + Tačno računanje putanja (sporo) + Računanje tačnih putanja bez grešaka. Radnja je ograničena daljinom i spora. + Da li sigurno želite da očistite odredište (i prolazna odredišta)\? + Podaci zvuka/videa + Izohipse + Senke brda + Vikipedija + Otvorene karte EU-a + Prednost putevima za motorna vozila. + Prednost… + Prednost autoputevima + neoznačeno + označeno + Vreme dolaska u međutačku + Vreme pristizanja + GPS podaci + Sloj sa osenčenim reljefima + Izmene OSM-a + Označi Omiljeno + Navedite adresu + Opcije pristupačnosti + Parrot + WunderLINQ + Tastatura + Nijedan + Odaberite uređaj za eksternu kontrolu, kao što je tastatura ili WunderLINQ. + Eksterni ulazni uređaj + Tema progama + Prilagodite izgled aplikacije. + Ugrađeno izdanje + Prikazuje podešavanja za uključivanje praćenja u pozadini i navođenja tako što se GPS uređaj periodično budi (a ekran ostaje ugašen). + Telefon + Veb strana + Kućni broj + Ime ulice + Nepovezan na bežičnu mrežu. Iskoristite trenutnu internet vezu za preuzimanje\? + Očisti odredište + Zaustavi navođenje + Odbaci putanju\? + Za isčitavanje kompasa, koristite magnetni senzor umesto orijentacionog senzora. + Smanjuje greške u iščitavanju kompasa, ali i unosi kašnjenje. + Započni novu podputanju + * Tapnite da označite tačku. +\n * Dugo držite da obrišete prethodnu tačku. +\n * Dugo držite na tačku da pogledate i dodate opis. +\n * Tapnite na spravicu za merenje da vidite još radnji. + Pravi rezervnu kopiju kako se OSM menja + Pravljenje rezervne kopije OSM izmena nije uspelo. + Generisan fajl %1$s sa OSM izmenama + Poređaj od vrata do vrata + Optimizuj redosled usputnih odredišta ka krajnjem odredištu. + Traži ulicu u okolnim gradovima + Izgled karte i podešavanja navigacije se pamte po korisničkom profilu. Postavite podrazumevani profil ovde. + Odredište + Australija + Oblast vožnje + Odaberite region vožnje: SAD, Evropa, Ujedinjeno Kraljevstvo, Azija i ostalo. + Odredi/uredi… + Ističe za (minuta) + Izvor pločica %1$s sačuvan + Podaci sličice: %1$s + Skinuto: %1$s + Skinutih zum nivoa: %1$s + O programu + Zumiranje karte + Na velikoj razdaljini + Na srednjim razdaljinama + Na bližim razdaljinama + Za duže destinacije: Dodajte usputna odredišta ako nijedan put nije nađen za ovu dugačku putanju ni posle 10 minuta. + Da li ste sigurni da želite da izbrišete %1$d OSM izmena\? + Avion + Brod + Motocikl + Planinarenje + Prikaz karte + Aplikativni profili + Izaberite profile za prikaz. + Vodena oznaka + Isključi komplikovano izračunavanje puta + Isključi dvofazno usmeravanje za navođenje u kolima. + Brzo izračunavanje puta nije uspelo (%s), prebacujem se na sporo izračunavanje. + Brzina izgovora + Odaberite brzinu izgovora sintetičkog TTS glasa. + Postavke putovanja + Postavke navođenja + Kamion + Računanje OsmAnd putanje van mreže + Umnožavam podatke OsMa… + Umnožavam podatke OsMa na novo odredište (%s)… + Kopiram fajl (%s) na novo odredište… + Od Androida 4.4 (KitKat), fasciklu za skladište (%s) je zastarela. Kopiraj sve OsmAnd fajlove na novi lokaciju za skladište\? +\n Beleška 1: Vaši stari fajlovi neće viti dirani (ali možete ih Vi sami obrisati). +\n Beleška 2: U novoj lokaciji za skladište, neće biti moguće deliti fajlove između OsmAnd i OsmAnd+ aplikacija. + Odredi visinu vozila koja je dozvoljena na putevima. + Ograničenje visine + Naznačite dozvoljenu širinu vozila na putu. + Ograničenje širine + Odredi dozvoljenu težinu vozila na putanji. + Ograničenje težine + Izbegavanje prelaženja državnih granica + Bez graničnih prelaza + Izbegavanje stepenica + Bez stepenica + Izbegavanjeautoputeva + Bez puteva za motorna vozila + Izbegavanje trajekata + Bez trajekata + Izbegavanje neasfaltiranih puteva + Bez neasfaltiranih puteva + Izbegavanje puteva sa naplatnim stanicama + Bez puteva sa naplatnim stanicama + Prednost putevima za motorna vozila + Prednost putevima za motorna vozila + Informacije o putu + Svojstva puta + Postavite odredište + Izaberite GPX… + Dodaj kao prolazno odredište + Koristi prikazanu stazu za navođenje\? + Računaj od prvog do poslednjeg odsečka staze OsmAnd-a + Računanje OsmAnd odsečaka staze van mreže + Evropa - Velika Britanija + Evropa - Italija + Severna Amerika - Kanada + Gvarani + Velški + Vijetnamski + Ukrajinski + Turski + Kineski (tradicionalni) + Švedski + Španski + Slovenački + Slovački + Kineski (hongkonški) + Kineski (pojadnostavljeni) + Svahili + Srpski (latinica) + Srpski (ćirilica) + Sardinijski + Ruski + Rumunski + Portugalski + Poljski + Persijski + Norveški bokmal + Marati + Litvanski + Letonski + Korejski + Kanada + Japanski + Italijanski + Indonezijski + Mađarski (zvanični) + Mađarski + Indijski + Hebrejski + Hebrejski + Ukazuj na pravac ciljne tačke trešnjom. + Uputstva trešenjem + Ukazuj na pravac ciljne tačke zvukom. + Zvučna uputstva + Pratite nas + Pomeri kartu radi izmena položaja oznake + Izmeni položaj oznake + Popunjenost baterije + Trenutna staza + Voz + Autobus + Oblik geografskih koordinata. + Oblik koordinata + Skladišna kartica + Otvoreno stalno + Traži + Sused + Oblast + iz + Poštanski broj + Kategorije + deljeno preko OsmAnd-a + Traži na rastojanju %1$s + Prikaži na karti %1$s + Ukloniti izabrane stavke iz „Istorije“\? + Nazad na pretragu + Pomorske karte + Pomorske izobate + Pomorske dubinske tačke severne polulopte + Pomorske dubinske tačke južne polulopte + Svetski Wikivoyage članci + Pomorske izobate + Hvala Vam na kupovini „Pomorskih izobata“ + Izobate mora (izohipse dubine) i karte pomorskih oznaka. + Pretplatite se na našu dopisnu listu za popust i dobijte još 3 preuzimanja karti! + Greška + Bez podvlačenja + Bez sloja nad kartom + Dugme za centriranje položaja ekrana na prvu međutačku ka odredištu. + Dugme koje centrira novo odredište na sredinu ekrana, i zamenjuje prethodno označeno odredište (ako postoji). + Dugme koje centar ekrana uzima za tačku polaska. Posle pita da odaberete odredište ili računa put do odredišta. + Dugme za centriranje odredišta na sredinu ekrana, a svako prethodno označeno odredište će postati poslednja međutačka. + Dodaj prvu prolaznu međutačku + Zameni odredište + Postavi odredište + Isključi samouvećanje karte + Uključi samouvećanje karte + Dugme za uključivanje/isključivanje automatskog zumiranja karte. + Uključiti/isključiti automatsko uvećanje karte + Pretraga po poštanskom broju + Izaberite grad + Najbliži gradovi + Ukucajte poštanski broj + Ukucajte grad/naselje/lokalitet + Ukucajte adresu + u %1$s + Izaberite ulicu + Sažetak + Uključi animaciju mog položaja za vreme navođenja. + Animiraj moj položaj + Promeni ime + Promeni boju + Ime grupe + Prikaz nivoa uveličanja: %1$s + Dozvoli pristup oblastima privatnih poseda. + Dozvoli pristup privatnim posedima + Prikaz počinje od nivoa uvećanja + Obrazac boja + Kupite i ugradite dodatak za izohipse da prikažete nagib vertikalnih oblasti. + Dodatak + Preuzmite kartu izohipsa za ovu oblast. + Sakrij počinjanje sa nivoa uvećanja + Instalirajte dodatak \"Izohipse\" da prikažete nagib vertikalnih oblasti. + Preuzmite kartu sloja sa senčenjem da biste videli reljef na karti. + Traži u Omiljenim + Razvrstano po udaljenosti + Dugme da prikažite ili da sakrijete OSM beleške na karti. + Promenite pretragu ili povećajte oblast pretrage. + Nema ničeg pronađenog + Povećaj oblast pretrage + Prepokreni pretragu + Vaše odredište se nalazi na privatnom posedu. Dozvoliti korišćenje privatnih puteva na ovom putovanju\? + Mrežne slike ulica za sve. Otkrijte mesta, sarađujte, osvojite svet. + Mapillary + Mrežne slike ulica za sve. Otkrijte mesta, sarađujte, osvojite svet. + Omogućava brzi doprinos Mapillary-ju. + Mapillary spravica + Podelite Vaš pogled sa ulice preko Mapilara. + Nema slika ovde. + Dodaj slike + Mrežne slike + Instalirajte program Mapillary da dodate slike na ovu lokaciju na karti. + Poboljšajte pokrivenost slika koristeći Mapillary + Instaliraj + Otvori Mapillary + Slika sa Mapilara + Rastojanje je ispravljeno + Ne mogu da uvezem fajl. Proverite da li OsmAnd ima dozvole za čitanje fajla. + Dozvole + Obim lenjira + Prikaži samo slike od 360° + Filtrirajte slike prema pošiljaocu, prema datumu ili prema tipu. Filtriranje se primenjuje samo pri zumiranju. + Korisničko ime + Pregledaj samo slike koje je dodao + Unesite korisničko ime + Dan + Pregledaj samo dodate slike + Od + Ka + Pogrešno korisničko ime + Ostavka pločica + Ponovo učitaj pločice radi postavljanja podataka dana. + Ponovo učitaj + Vrati na podrazumevano + Sačuvaj snimljene putanje u podfasciklama prema mesecima snimanja (npr. 2018-01). + Sačuvaj snimljene putanje u mesečnim fasciklama + Dugme za početak ili završetak navođenja. + Počni/zaustavi navođenje + Izaberite profil navođenja + Sačuvajte tačke ili kao tačke puta, ili kao liniju. + OsmAnd će se povezati tačke sa putevima izabranog profila. + Opcije + Dodaj tačku posle + Dodaj tačku pre + Uredi liniju + Tačka puta + Sačuvaj kao liniju + Sačuvaj kao tačke puta + Linija + Izađi pre čuvanja\? + Prikazuj na karti + Isključuje animacije u aplikaciji. + Bez animacija + Rasporedi po + Prikaz udaljenosti + Sve premesti u istoriju + Ove godine + Poslednje sedmice + Juče + Danas + Aktiviraj + Poslednje korišćeno: %1$s + Grupe + Spisak + Oznaka karte je premeštena u aktivne + Oznake karte su premeštene u istoriju + Sve oznake karte su premeštene u istoriju + Ispod izaberite kojom brzinom će promena usmerenja preći iz „u pravcu kretanja“ na „po pravcu kompasa“. + Zadrška usmerenja karte + Izaberite kako ukazati na rastojanje i pravac ka oznakama karte na karti: + Rasporedi po: + Dodato + A-Z + Z-A + Ukloni iz ’Oznaka karti’ + Sakrij pređene + Prikaži pređene + Prikazuj strelice na karti + Prikaži vođice + Grupa će nestati posle sledećeg pokretanja programa. + Premesti u istoriju + Izvezite oznake u sledeći GPX fajl: + Unos koordinata + Sortiraj + Plan putovanja + Kraj + Moj položaj + Dodajte svoj položaj kao tačku polaska da isplanirate savršeno putovanje. + Koristi položaj + Izbegavanje puteva sa poledicama i plićake. + Bez puteve sa poledicama ili plićacima + Brzi unos koordinata + Izaberite oblik unosa koordinata. Možete uvek da ga promenite tapkanjem na \"Opcije\". + Koristi sistemsku tastaturu + Oblik koordinate + Oznake + Napravi povratno putovanje + Dodajte kopiju tačke početka kao odredište. + Izmenite belešku OSM-a + Izmeni belešku + Ne mogu da izmenim belešku. + Morate dodati najmanje jednu oznaku za korišćenje ove mogućnosti. + Povratno putovanje + Put sračunat + Prikaži kartu + Put + Pogrešan oblik + Prolazne tačke dodate oznakama na karti + Pregled + Nazad + Unesite novo ime + Pogrešan unos + Uvezi fajl + Uvezi kao Omiljeno + Uvezi kao GPX fajl + može se uvesti ili kao Omiljene tačke ili kao GPX fajl. + Oznaka prođena + Režim preko celog ekrana + Tupkanje na kartu je uključuje i isključuje kontrolna dugmad i spravice. + Preimenuj oznaku + Sledeće polje + Nalepi + Prikazuj brojčanu stavku + Levo + Desno + Broj decimalnih brojki + Odaberite broj pokazivača pravca. + Izaberite način prikaza rastojanja do aktivnih oznaka. + Ne koristi + Da + Otkaži + U redu + + Opšte postavke + Postavke navođenja + Često postavljana pitanja, skorašnje izmene i ostalo. + Šir %1$s +\nDuž %2$s + Prazan spisak + Oponašaj svoj položaj + Poseti pre + Poseti nakon + Vaše izmene + preostalo + Vreme parkiranja je ograničeno na + min + h + OSM izmene + Dejstvo brisanja + Dejstvo izmene + Dejstvo stvaranja + zakasneo + Idi + Pokreni navođenje skretanja nakon… + Odredite preostalo vreme za čekanje ekrana za iscrtavanje putanje. + Očistiti istoriju\? + Da li ste sigurni da želite da otpremite %1$d izmena na OSM\? + Da li ste sigurni da želite da izbrišete %1$d beleški\? + Označi za brisanje + Izmene OSM-a dodate skupu mesnih izmena + Trenutna putanja + Dobrodošli + OsmAnd ima mogućnost pregleda zemaljskih karata van mreže i navođenja van mreže svetom. + Izaberite svoju regiju radi pravilnog čitanja saobraćajnih znakova i propisa: + Preuzmi karte + U mnogim državama (Nemačkoj, Francuskoj, Italiji i dr.) je upotreba pokretne kamere zabranjena zakonom. OsmAnd nije odgovoran za korisnikovo kršenje zakona. Molim, kliknite na \"da\" samo ako je za Vas ova mogućnost snimanja zakonita. + Podaci A-GPS-a preuzeti: %1$s + Poruka + Prikazuj opise. + Traži + Mesta + Uredi + Upravljaj + Obaveštenja A-GPS-a + Pomoćnik karata OSM-a + Otvoreno sad + Kucajte za pretragu + Filtriraj po imenu + Umnoži + Skladište karti + Fascikla skladišta podataka + Unutrašnje skladište + Ručno određeno + Unutrašnja memorija aplikacije + Skladište za više korisnika + Spoljne skladište + Premeštanje fajlova nije uspelo + Nemoguće je obrazovati karte u određenoj fascikli + Premestiti OsmAnd fajlove podataka na novo odredište\? + Kućne brojeve + Odjavi se + Bojenje prema OSMC planinarskom simbolu + Bojenje prema vrsti puta + Isključen + Izbriši OSM izmene + Prikaži detalje + Vikipedija + Vikipedija + Čitaj potpuni članak (na mreži) + Uvoz u OsmAnd + Spreči automatsko beleženje + Usluga određivanja položaja je isključena. Uključiti je\? + Preuzeti dodatne podatke sa Vikipedije (%1$s MB)\? + Imate stare nekompatibilne podatke sa Vikipedije. Da ih arhiviramo\? + Berberski + Kabilski + Gornjolužičkosrpski + Asturijski + Sebuano + Estonski + Galicijski + Haićanski + Malezijski + Nevarski + Oksitanski + Novonorveški + Telugu + Tajlandski + Volapuk + Norveški bukmol + Argentinski španski + Američki španski + Esperanto + Osetski + Laoski + Luksemburški + Malajalam + Tamilski + Kurdski + Latinski + Irski + Navaho + Bišnuprijski + Islandski + Albanski + Bretonski + Azerski + Srpskohrvatski + Tagalog + Bengalski + Pijemontanski + Tamna + Svetla + Potrebno je ponovno pokretanje programa za primenu izmena. + Prozirna ljubičasta + Crna + Smeđa + Prozirna ružičasta + Ružičasta + Ljubičasta + Prozirna plava + Plava + Prozirna svetlo plava + Svetlo plava + Prozirna zelena + Zelena + Prozirna svetlo zelena + Svetla zelena + Prozirna žuta + Žuta + Prozirna narandžasta + Prozirna crvena + Crvena + Tamno-žuta + Širina GPX traga + GPX širina + GPX boja + GPX boja + Podrazumevano (prozirna zelenkasto-plava) + Podrazumevano (13) + Nadogradnje uživo + Nema dostupnih nadogradnji + Zadebljaj obrisne linije + Opasnost + Izbegavanje šatl vozova + Bez šatl vozova + Dodaci + Položaji + GPX fajl sa položajima. + Pretražujem adresu + Simulirajte Vašu poziciju koristeću snimljenu GPX stazu. + Oponašaj upotrebom proračunate putanje ili GPX snimka. + Zaustavi oponašanje svog položaja. + Omiljena obaveštenja + Međuvreme buđenja GPS-a + Radi u pozadini + Navođenje + Snimanje putovanja + m/s + min/km + min/m + čv + Nautičkih milja na sat (čvorovi) + Minuta po milji + Minuta po kilometru + Metara u sekundi + Milja na sat + Kilometara na sat + Nautičke milje + nmi + Jedinica za brzinu + Odredite merne jedinice za brzinu. + Odaberite kategoriju + Dodaj novo + Izohipse su isključene + Sloj senčenja padina je isključen + Svetske karte + Karte velikih oblasti + Oblasti + Dodaj novu kategoriju + Ime kategorije + Upotrebite ime kategorije koje već ne postoji. + Šalji + Deli svoj položaj + položaj: + Podražavaj prvo pokretanje programa + Postavlja indikator kao da se OsmAnd pokreće prvi put, sve ostale postavke ostavlja nepromenjenim. + Prikaži kartu + Karta %1$s je spremna za korišćenje. + Prikazuj jednu ili dve strelice koje pokazuju pravac aktivnih oznaka. + Prikazuj vođice od svog položaja do položaja aktivne oznake. + Jedan + Dva + Oznake označene kao pređene će se pojaviti na ekranu. + Uvozite Omiljene grupe ili prolazne tačke kao oznake. + Uvezi grupe + Dugo ili kratko pritisnite na „Mesta“, onda pritisnite zastavicu kad se pojavi. + Napravite oznake na karti! + Uvozite grupe iz Omiljenih ili iz GPX prolaznih tačaka. + Dodaj skup + Omiljena kategorija + Prolazne tačke putanje + Izaberite kategoriju Omiljenih za dodavanje u oznake. + Odaberite sa koje putanje da dodate prolazne tačke na oznake. + Izgled na karti + Još + Pretraga za putanjama sa prolaznim tačkama + Po vrsti + Po danu + A/V beleške po datumu + Dodajte zvučnu, video ili slikovnu belešku na svaku tačku karte koristeći vidžete ili kontekstne menije. + Pravite beleške! + Pokretanje na \'jedan tup\' + Tupnite oznaku na karti radi njenog pomeranja na vrh aktivnih oznaka bez otvaranja priručnog izbornika. + Aktivirana oznaka %s. + Veb adresa + Interval praćenja + Vremenski bafer + Preporuka: Postavka od 5 metara ako ne treba da beležite preciznije detalje od ovoga i nedvosmisleno ne želite da beležite podatke prilikom odmaranja. + Nuspojave: periodi odmaranja se ne snimaju uopšte ili se ne snima samo po jedna tačka. Mala (stvarna) pomeranja (npr. u stranu, da obeležite moguće skretanje na Vašem putu) mogu da budu isfiltrirana. Vaš fajl sadrži manje informacija za naknadnu obradu i ima lošije statistike time što se filtriraju očigledno redudantne tačke za vreme snimanja, dok se potencijalno čuvaju artifakti koji nastaju lošim signalom ili efektima GPS čipa. + Ovaj filter izbegava da se snimaju tačke duplikati kada ima malo stvarnog kretanja, i daje bolji izgled staze na karti. + Opaska: Ako je GPS bio isključen odmah pre snimanja, prva tačka će imati umanjenu tačnost, pa u našem kodu bi želeli da sačekamo par sekundi pre snimanja tačke (ili snimanja najbolje od 3 uzastopne tačke itd.), ali ovo još nije napravljeno. + Preporuka: teško je predvideti šta će se beležiti, a šta neće, pa je najbolje da isključite ovaj filter. + Nuspojava: Kao rezultat filtriranja po tačnosti, neke tačke mogu skroz nedostajati npr. ispod mostova, drveća, između visokih zgrada, ili u određenim vremenskim uslovima. + Ovo će snimati samo tačke izmerene sa naznačivanjem minimalne tačnosti (u metrima/stopama, kao što prijavljuje Android za Vaš čipset). Tačnost je približna merenjima stvarne pozicije, i nije direktno vezana za preciznost, koja je rasipanje ponovljenih merenja. + Opaska: provera brzine > 0: većina GPS čipova prijavljuje vrednost brzine samo ako algoritam primeti da ste u pokretu i ništa ukoliko niste. Korišćenje postavke >0 u ovom filteru je zapravo korišćenje senzora pokreta iz GPS čipa. Čak iako se ne filtrira ovde za vreme snimanja, mi i dalje koristimo ovu funkcionalnost u našoj GPX analizi da odredimo pređenu udaljenost, tj. vrednost koju prikazujemo u tom polju je pređena udaljenost za vreme kretanja. + Preporuka: Pokušajte prvo da koristite detektor kretanja preko filtera minimalnog pomeraja (B) prvo, pošto može dati bolje rezultate i izgubićete manje podataka. Ako Vaše staze ostanu pune šuma pri malim brzinama, probajte sa ne-nultim vrednostima ovde. Pazite da neki tipovi merenja neće uopšte davati merenja brzine (neki metodi preko mreže) u kom slučaju nećete snimiti ništa. + Nuspojave: Vašim stazama će nedostajati sve sekcije gde kriterijum minimuma brzine nije bio ispunjen (npr. gde ste gurali bicikl uzbrdo). Takođe, neće biti informacija o periodima mirovanja, kao što su pravljenje pauza. Ovo može da ima efekta na analizu ili post-procesiranje, kao što je određivanje ukupne dužine puta, vremena kretanja, ili prosečne brzine. + Ovo je filter za odsecanje malih brzina koji ne beleži tačke ispod određene brzine. Ovim snimljene staze izgledaju glatkije kada se gledaju na karti. + Umesto toga odaberite podržani %1$s fajl ekstenzije. + Nema pravila za rutiranje u ’%1$s’. Odaberite drugi fajl. + Proverite i podelite detaljne zapise aplikacije + Potrebne su dozvole za korišćenje ove opcije. + Pored profila, možete odabrati dodatne podatke za izvoz. + Uvezeni profil sadrži dodatne podatke. Kliknite na Uvoz da uvezete samo profilne podatke ili odaberite dodatne podatke. + Uključi dodatne podatke + Stil iscrtavanja + Rutiranje + Sunce izlazi u %1$s + Sunce zalazi u %1$s + %1$s/%2$s + Sve postavke profila vraćene na podrazumevano. + Sve postavke dodatka vraćene na podrazumevano. + Prikaži samo noću + Dodaj proizvoljnu kategoriju + Dostupno + Možete da dodate nove proizvoljne kategorije označavajući jednu ili više kategorija. + Promenite redosled sortiranja spiska, sakrijte kategorije. Možete uvesti i izvesti sve promene sa profilima. + Preuredi kategorije + Režim pristupačnosti je isključen na Vašem Android sistemu. + Podrazumevano je isključeno: Dok OsmAnd radi u prednjem planu, ekran neće da se gasi. +\n +\nAko je uključeno, OsmAnd će koristiti sistemsko vreme odlaganja gašenja ekrana. + Koristi sistemsko vreme odlaganja gašenja ekrana + Resetovanje na podrazumevano će postaviti redosled sortiranja na podrazumevano stanje posle instalacije. + Kopiraj koordinate + • Profili: sada možete promeniti redosled, postaviti ikonicu za kartu, promeniti sve postavke osnovnih profila i povratiti ih nazad na podrazumevane vrednosti +\n +\n • Dodati brojevi izlaza prilikom navođenja +\n +\n • Preuređene postavke dodataka +\n +\n • Preuređen ekran Postavki za brži pristup svih profilima +\n +\n • Dodata opcija za kopiranje postavki iz drugog profila +\n +\n • Dodata mogućnost da se promeni redosled ili da se sakriju kategorije tačaka od interesa u Pretrazi +\n +\n • Ispravno poravnate ikonice tačaka od interesa na karti +\n +\n • Dodati podaci izlaska i zalaska sunca na Podešavanju Karte +\n +\n • Na kartu dodata ikonica Kuća/Posao +\n +\n • Dodata podrška za opise iz više redova u Postavkama +\n +\n • Dodata ispravna transliteracija na kartu Japana +\n +\n • Dodata karta Antarktika +\n +\n + Očisti snimljene podatke + Dikretno-na-tačku + Put će biti preračunat ako je udaljenost do puta veća od odabranog parametra + Minimalna udaljenost za preračunavanje puta + %1$s — %2$s — %3$s + Meni + Ovaj dodatak je posebna aplikacija, morate ga posebno ukloniti ako ne planirate da ga koristite. +\n +\nDodatak će ostati na uređaju i posle uklanjanja OsmAnd aplikacije. + Dodatak isključen + Otvori postavke + Bez preračunavanja + Unesite ime profila + Odaberite podatke za uvoženje. + Neke stavke već postoje + OsmAnd već ima elemente sa istim imenom kao oni koji se uvoze. +\n +\nOdaberite radnju. + Uvežene stavke će biti dodate uz prefiks + Zadrži obe + Zameni sve + Trenutne stavke će biti zamenjene stavkama iz fajla + Izlistan %1$s, već postoji u OsmAnd-u. + Profili + Brze radnje + Ništa nije označeno + Tipovi tačaka od interesa + Prepremam + Podrazumevano u aplikaciju (%s) + Minimalni ugao između moje lokacije i puta + Totalno pravi segment između moje lokacije i izračunatog puta biće prikazan dok se put ne izračuna + Ugao + Ugao: %s° + Proizvoljni profil + Uvozim fajl za iscrtavanje + Teren + Mapa senka brda koristi tamne nijanse za prikazivanje nagiba, vrhova i nizina. + Pri nagibu koriste se boje za vizuelizaciju strmine terena. + Postavite minimalni i maksimalni nivo zuma, pri kome će sloj biti prikazan. + Dodatne mape su potrebne da biste videli senke brda na mapi. + Dodatne mape su potrebne da biste videli nagibe na mapi. + Možete pročitati više o nagibima u %1$s. + Providnost + Nivoi uvećanja + Legenda + Omogućite da bi videli senke brda ili mape nagiba. Možete pročitati više o ovim vrstama karta na našem sajtu. + Senke brda + Svi podaci iz %1$s su uvezeni, možete da upotrebite dugmad ispod da bi ste otvorili neophodni deo aplikacije radi upravljanja njima. + Uvoz završen + Dodate stavke + Osmand proverava %1$s postojanje duplikata sa postojećim elementima u aplikaciji. +\n +\nTo može da potraje neko vreme. + Uvozim + Uvozim podatke iz %1$s + Ne mogu da napravim rezervnu kopiju profila. + Čuvam novi profil + Povrati sve postavke profila\? + Sve postavke profila će biti vraćene na originalno stanje pre kreiranja/uvoženja ovog profila. + Da li ste sigurni da želite da očistite snimljene podatke\? + Preračunaj put u slučaju odstupanja + Odaberite udaljenost posle koje se put preračunava. + Put će biti preračunat ukoliko je udaljenost trenutne pozicije od puta veća od odabrane vrednosti. + %1$s od %2$s + Nagibi + Dugme da prikaže ili sakrite sloj terena na karti. + Obriši opis + Dodaj opis + Odaberite grupu + Odaberite oblik + Krug + Oktagon + Kvadrat + Min + Prilagodite količinu elemenata u \"Fioci\", \"Podešavanju mape\" i \"Kontekst meniju\". +\n +\nOnemogućite nekorišćene dodatke da sakrijete sve njihove kontrole. %1$s. + Elementi Fioke, Kontekst meni + Prilagođavanje korisničkog interfejsa + Fioka + Akcije Kontekst menija + Preuredi ili sakrij stavke sa %1$s. + Razdelnik + Elementi ispod ove tačke su odvojeni razdelnikom. + Sakrivena + Iako su ove stavke sakrivene sa glavnog menija, opcije ili dodaci koje oni predstavljaju i dalje rade. + Sakrivanje postavki ih resetuje na originalno stanje. + Ima samo četiri dugmeta. + Glavne radnje + Možete pristupiti ovim radnjama kliktanjem na dugme „%1$s”. + Možete pomerati stavke samo unutar ove kategorije. + Dodatak za programere + Zameni drugu tačku sa ovom. + Ski turing + Snoumobil + Proizvoljni OsmAnd dodatak + Stavke + Izmene primenjene na ’%1$s’ profil. + Ne mogu da čitam iz „%1$s”. + Ne mogu da pišem %1$s. + Ne mogu da uvezem iz „%1$s”. + Izaberite fajl staze + Jezici + Jezik + Svi jezici + Za prikaz Vikipedijinih tačaka od interesa su potrebne dodatne karte. + Odaberite jezik za Vikipedijine članke na karti. Prebacite na bilo koji drugi dostupni jezik dok čitate članak. + Neki članci sa Vikipedija su možda dostupni na Vašem jeziku. + Kantonski + Južnominski + Joruba + Varajski + Uzbečki + Urdu + Tatarski + Tadžički + Škotski + Sicilijanski + Pandžabi + Nepalski + Napolitanski + Burmanski + Mongolski + Minangkabajski + Malgaški + Kirgiski + Kazaški + Javanski + Gudžarati + Čuvaški + Čečenski + Bavarski + Baškirski + Aragonski + Lombardijski + Proizvoljna boja + %1$s / %2$s + Naplatom će biti opterećen vaš Gugl Plej nalog pri potvrdi kupovine. +\n +\nČlanarina se automatski obnavlja ukoliko nije otkazana pre datuma obnove. Vaš nalog biće zadužen periodom obnove (mesec / tri meseca / godinu dana) samo na dan obnove. +\n +\nČlanarinama možete upravljati i otkazati ih tako što ćete otići na vaša Gugl Plej podešavanja. + Pretraga tipova tačaka od interesa + Kombinujte tipove tačaka od interesa iz različitih kategorija. Kliknite prekidač da odabere sve, kliknite na levu stranu da odaberete kategoriju. + Dodatne karte + Nepodržana radnja %1$s + OsmAnd pratilac + OsmAnd + Mapillary + Brze radnje + Lenjir prečnika + Izmeri udaljenost + Putovanje (Wikivoyage i Vikipedija) + Markeri na karti + Omiljeni + Članarina - OsmAnd uživo + OsmAnd kupovine + Uputstvo za legendu karte. + Profili navođenja + • Nove oflajn mape nagiba +\n +\n • Puno prilagođavanje Favorita i GPKS tačaka – prilagođavanje boja, ikona, oblika +\n +\n • Prilagođavanje redosleda elemenata \"Kontekst menija\", \"Podešavanje mape\" i \"Kutije\" +\n +\n • Vikipedija kao poseban sloj u Podešavanju mape, izaberite samo potrebne jezike +\n +\n • Napravite svoj TOI filter / mapu sa punom fleksibilnošću +\n +\n • Dodata mogućnost da se povrate podešavanja korisničkih profila +\n +\n • Celovite GPKS rute sa podrškom za saobraćajne trake i celovita uputstva skretanja +\n +\n • Ispravljene veličine korisničkog interfejsa na tabletama +\n +\n • Ispravljeni bagovi sa RTL +\n +\n + Dugme koje prikazuje ili skriva javni prevoz na karti. + Napravite ili izmenite POI + Parking pozicije + Kreirajte putanje tapkanjem na kartu, ili upotrebom i izmenom postojećih GPX fajlova, da biste isplanirali put i merili rastojanja između tačaka. Rezultat se može sačuvati kao GPX fajlovi za kasnije navođenje. + Omiljeno + Omiljeno + Karta + Prikaži na karti + Sklopi + Izlaz + Zatvori + Dejstvo {0} + Neočekivana greška + Ulazno-izlazna greška + Preuzeto + Preuzimam… + Osveži + Još dejstava + Još… + Izvezi + Zaustavi + Primeni + Deli + Izbriši + Očisti sve + Očisti + Označi sve + Odznači + Označi sve + Izaberi na karti + Istorijat + Postavke + Pomoć + Ništa + izabrano + Izabrano + Isključen + Uključen + Onemogući + Omogući + Sledeće + Prethodno + Isključeno + Uključeno + Ne + Napredna pretraga koordinata + Pretraga koordinata + %1$s stajanja pre + Počnimo + Izmeni + Nadzemne građevine + Da li ste sigurni da želite zameniti Omiljeni %1$s\? + Put koji štedi gorivo + Očisti sve pločice + Osvežiti sve karte sada\? + Pronađi moj položaj + Uzmi uputstva i otkrij nova mesta, i bez interneta + Omogućite pristup određivanju položaja + Dajte dozvole + OsmAnd skladište podataka (za karte, fajlove staza itd.): %1$s. + Slobodno mesto skladišta + Pretražujem položaj… + Zahtevane za preuzimanje. + Nema mrežne veze + Nije prepoznat položaj + Pusti da OsmAnd odredi Vaš položaj i da prema tome predloži karte za preuzimanje te oblasti. + Pretražujem karte… + Odaberite drugu oblast + Nemate ugrađenih karata. Možete izabrati kartu sa spiska ili preuzeti karte kasnije preko \'Meni - %1$s\'. + Preskoči preuzimanje karata + Milje/metri + Neograničena preuzimanja karti, nadogradnje, dodatak za Vikipediju. + Dobavite neograničeno preuzimanje karata, uz sedmična, dnevna i čak časovna ažuriranja. + Dobavite za %1$s + Dobavite je + Članarina omogućava časovne, dnevne i sedmične nadogradnje, i neograničena preuzimanja svih karata. + Deo Vaše članarine će biti poslat korisnicima OSM-a. Članarina ostaje ista. + Prilog OSM zajednici + Članarina se naplaćuje po odabranom periodu. Možete je otkazati na Gugl pleju kad god poželite. + Molimo, unesite ime novog filtera koji će biti pridodat jezičku „Kategorije“. + Novi filter + Izbriši filter + Sačuvaj filter + Primeni filtere + Filteri + Proizvoljna pretraga + Napravi proizvoljni filter + Izabrane kategorije + Potkategorije + Izmeni kategorije + Preslovi imena + Preslovi ako %1$s prevod ne postoji + Ukucajte grad, adresu, ime tačke od interesa + Celovitost površi puta + Obrazac boja linija izohipsi + Tamna smeđa + Svetla smeđa + Prikazuj božićne tačke od interesa\? + U čekanju Božića i Nove Godine, možete izabrati prikaz tačaka od interesa vezanih za Božić: Badnjake, jelke, trgovine, itd. + Božićne tačke + Filter: Nema zapisa dok se ne dostigne ova tačnost. + Najmanja tačnost beleženja + Filter: Postavi minimalnu udaljenost od tačke da bi se beležila nove tačke. + Najmanji pomeraj beleženja + Filter: Nema zapisa tačaka ispod ove brzine. + Najmanja brzina beleženja + Obrazac boja linija izohipsi + Nema podataka + Snimaj + Snimljeno + Putovanje + Pauzirano + Pauziraj + Nastavi + Nastavi + Obaveštenja + Prikazuj obaveštenje koje omogućava pokretanje snimanja putovanja. + Uključi brzo snimanje + Izgled + Dodaj još… + Takođe, možete dodati fajlove staza u fasciklu + Još uvek nemate nijednu datoteku staza + Proračun putanje + Otpremi tačku od interesa + Odredi %1$d izlaz i idi + Grad ili regija + Članci Vikipedije o okolini + Otpremajte svoju belešku OSM-a anonimno ili korišćenjem svog naloga na OpenStreetMap.org-u. + Dozvoli autoputeve. + Počni novi odsečak posle razmaka od 6 minuta, novu putanju posle razmaka od 2 sata, ili novi fajl posle dužeg razmaka ako je dan izmenjen. + Samopodeli snimke posle razmaka + Pomorske izobate + Prikazuj tačke i izobate. + Koristi podatke o visini + Izaberite visinsko odstupanje + Ukupno rastojanje + Vreme + Prosečna visina + Raspon visine + Uzlazan + Silazan + Uspon puta + Način vožnje + Putanje + Premesti + Ne mogu da premestim fajl. + Izaberite fasciklu GPX fajlova + Boja + Vreme dolaska + Vreme polaska + Najviši + Vremenski raspon + Vreme kretanja + Prosečna brzina + Najveća brzina + Putanja + Istaknute tačke na ovom putu + Broj skretanja na ovom putu + Da li ste sigurni da želite da izbrišete %1$d tačku(i)\? + Tačka(e) izbrisana(e). + Dodaj novu fasciklu + Nagib + Omiljeni teren: pavno ili brdovito. + Preferiraj sporedne + Uravnoteženo + Kraće rute + Ravno + Manje brdovito + Brdovito + Hvala Vam za kupovinu plaćenog izdanja OsmAnd-a. + Opcije parkiranja + Ne prikazuj ponude popusta iz programa i specijalne poruke o lokalnim događajima. + Ne prikazuj poruke pri pokretanju + OsmAnd prikuplja podatke o tome koje delove programa otvarate. Vaš položaj se ne šalje, niti išta što unesete u program niti pojedinosti o oblastima koje vidite, tražite ili preuzmete. + Ne šalji bezimene podatke o korišćenom programu + Automatsko + Vožnja desnom trakom + Fontovi na karti + Povrati trgovinu + Vidljivo + Raščlani na karti + Dodajte ili izmenite omiljeno + Povrati podrazumevani poredak stavki + Povratak uređivanju + Akciono dugme menja između izabranih profila. + Dodaj profil + Promeni profil aplikacije + Ne može se naći takav profil. + Svetska opšta mapa (detaljna) + Nepodržani tip + Unesite širinu vozila, neka ograničenja puta mogu biti primenjena za široka vozila. + Unesite visinu vozila, neka ograničenja puta mogu biti primenjena za visoka vozila. + Unesite težinu vozila, neka ograničenja puta mogu biti primenjena za teška vozila. + Unesite dužinu vozila, neka ograničenja puta mogu biti primenjena za duža vozila. + OsmAnd GPX nije dobro formiran, molimo obratite se timu podrške radi dalje istrage. + Uvek> + Kontrola ekrana + Isključi ekran prema sistemskom vremenu zaključavanja. + Koristi sistemsko vreme zaključavanja ekrana + Odaberite opcije paljenja ekrana (OsmAnd treba da bude iu prednjem planu kada se uređaj zaključa): + Svaka instrukcija navođenja će upaliti ekran. + Instrukcije navođenja + Onemogućeno. Zahteva \'Drži ekran upaljenim\' pod \'Tajmaut posle buđenja\'. + Pritiskanjem dugmeta za gašenje uređaja ćete upaliti ekran sa OsmAnd-om preko zaključanog ekrana. + Dugme za gašenje + Senzor blizine + Odaberite tajmaut ekrana posle buđenja. (\"%1$s\" se odnosi na tajmaut.) + Drži ekran upaljenim + Drži ekran ugašenim + Pseudo-Merkator projekcija + Jedan fajl po sličici + SQLiteDB fajl + Unesite ime za ovaj mrežni izvor karte. + Unesite ili nalepite adresu mrežnog izvora. + Izmeni mrežni izvor + Vreme isteka + Merkator projekcija + Format skladišta + Postavite minimalni i maksimalni nivo uveličavanja koji prikazuje ili učitava mrežnu kartu. + Utiče na ekran kada se koristi kao mapa ili podsloj/nadsloj. +\n +\n%1$s: Mapa je ograničena odabranim nivoom zuma. +\n +\n%2$s: su nivoi pri kojima će originalne pločice biti vidljive, a umanjenje ili uvećanje biće izvan ovih vrednosti. + Iskeširane sličice će biti ponovo preuzete posle zadatog broja minuta. Ostavite prazno da nikad ne osvežavate sličice iz ovog izvora. +\n +\nJedan dan je 1440 minuta. +\nNedelju dana je 10 080 minuta. +\nMesec dana je 43829 minuta. + Odaberite kako da skladištite preuzete sličice. + Možete izvoziti ili uvoziti brze radnje sa aplikativnim profilima. + Obriši sve\? + Da li želite bespovratno da obrišete %d brzih radnji\? + Vreme iksljučivanja ekrana + Izbriši radare + Pravno + Radarske tačke od interesa + U nekim državama ili oblastima, korišćenje aplikacija za obaveštavanje o radarima na putu je zabranjeno zakonom. +\n +\nMorate napraviti izbor ovde, u zavisnosti od zakona Vaše države. +\n +\nOdaberite %1$s i primažete obaveštenja i upozorenja o radarima i kamerama za brzinu. +\n +\nOdaberite %2$s. Svi podaci vezani za radere: upozorenja, obaveštenja, tačke od interesa će biti obrisane dok se OsmAnd ponovo ne instalira. + Drži aktivnim + Izbriši + Upozorenja za radare su zakonom zabranjene u nekim državama. + Ako je „%1$s” uključen, vreme aktivnosti će zavisiti od toga. + Podrazumevano vreme iksljučivanja ekrana + tona + metara + Prikaži ili sakrij dodatne detalje karte + Noćna karta + Dodaj mrežni izvor + Primenom ovih izmena ćete očistiti iskeširane podatke za ovaj izvor sličica + Podesi visinu plovila + Možete podesiti visinu plovila da izbegnete niske mostove. Imajte na umu da, ukoliko most može da se pomera, koristićemo njegovu visinu kada je otvoren. + Podesite visinu plovila da izbegnete niske mostove. Imajte na umu da, ukoliko most može da se pomera, koristićemo njegovu visinu kada je otvoren. + Podesite širinu plovila i izbegnite uske mostove + Prikaži ili sakrij Mapillary sloj na karti. + Odaberite dužinu vozika koja je dozvoljena na putevima. + Limit dužine + Kurs + %1$s obrisano + Ponovo pokrenite aplikaciju da biste izbrisali sve podatke brzinskih kamera. + Deinstaliraj i ponovo pokreni + Ovaj uređaj nema radare. + Roleri + Obriši najbližu odredišnu tačku + Kontrolišite nivo zumiranja na mapi pomoću dugmića za jačinu zvuka na uređaju. + Dugmići za jačinu zvuka za zumiranje + Molimo odredite ime tačke + Uklanja trenutnu odredišnu tačku. Ako je ona i završna tačka, navođenje će se zaustaviti. + Preuzmite mape Vikipedije + Informacije o tačkama od interesa potražite na Vikipediji, džepnom vanmrežnom vodiču koji ima članke o mestima i odredištima. + Dodata tačka neće biti vidljiva na mapi, pošto je odabrana grupa sakrivena, možete je pronaći u „%s“. + Enduro skuter + Skuter + Invalidska kolica + Invalidska kolica napred + Korpa + Zatvorena OSM beleška + Za nastavak odredite radne dane + Ruta između tačaka + Planiraj rutu + Dodajte stazi + Prikaži početne i krajne ikone + Izaberite širinu + Izaberite interval na kome će se prikazivati oznake sa razdaljinom ili vremenom na stazi. + Izaberite željenu opciju podele: po vremenu ili po udaljenosti. + Prilagođen + Strelice smera + Čvrst + Poslednje izmenjena + Uvezi putanju + Otvori postojeću putanju + Kreiraj novu rutu + Izaberite fajl putanje za otvaranje. + Završeno + Zameni stazu + Sačuvaj kao novu stazu + Obrnuta ruta + Čitava staza će se preračunati koristeći odabrani profil. + Samo će sledeći segment biti preračunat koristeći odabrani profil. + Izaberite kako povezati tačke, po pravoj liniji, ili izračunajte rutu između njih kako je dole naznačeno. + Cela staza + Sledeći segment + Zatim pomoću jednog od vaših navigacionih profila prislonite stazu na najbliži dozvoljeni put da biste koristili ovu opciju. + Prag udaljenosti + Navigacijski profil + Izaberite datoteku zapisa kojoj će se dodati novi segment. + Slike na nivou ulice + Da li ste sigurni da želite da odbacite sve promene na planiranoj ruti\? + U slučaju obrnutog pravca + Sačuvaj kao novu datoteku staze + Dodaj u datoteku staze + Putanje + Putanje + Putanje + Upisuj putanju u GPX fajl + Trasa rute + Dodajte datoteke staza + Uvezite ili snimite datoteke staza + Dodajte tačku staze + Dodajte tačku staze + Snimanje putovanja + Sačuvaj kao datoteku staze + Sledi stazu + Izaberite datoteku staze radi praćenja + Izaberite datoteku staze radi praćenja ili uvoza sa vašeg uređaja. + Izaberite drugu stazu + Navodite od moje pozicije do staze + Tačka staze za navigaciju + Početak staze + Najbliža tačka + Pričvrstite na puteve + Izbrišite adresu + Dodajte adresu + Unesite adresu + Skrati pre + Skrati posle + Promenite vrstu rute pre + Promenite vrstu rute nakon + Pojednostavljena staza + Samo linija rute će biti sačuvana, a putne tačke će biti izbrisane. + Ime fajla + %s izabranih datoteka staza + REK + Evidentiranje staze će biti pauzirano kada se aplikacija ubije (preko skorašnjih programa). (Indikator rada OsmAnd-a u pozadini tada nestaje iz obaveštajne trake.) + Navedite interval evidentiranja za opšte snimanje staza (uključeno pomoću vidžeta „Snimanje putovanja“ na mapi). + Pauziraj snimanje puta + Nastavite snimanje putovanja + Sistemsko podrazumevana + Svi naredni segmenti + Prethodni segment + Svi prethodni segmenti + Samo izabrani segment će se ponovo izračunati pomoću izabranog profila. + Svi naredni segmenti će se ponovo izračunati pomoću izabranog profila. + Svi prethodni segmenti će se ponovo izračunati pomoću izabranog profila. + Otvori sačuvanu + je sačuvano + Dodajte bar dve tačke. + Ponovi + • Ažurirano Funkcija planiranja rute: omogućava upotrebu različitih tipova navigacije po segmentu i uključivanje staza +\n +\n • Novi meni Izgled za staze: odaberite boju, debljinu, strelice smera prikaza, ikone početka i završetka +\n +\n • Poboljšana vidljivost biciklističkih čvorova. +\n +\n • Staze se sada mogu dodirnuti, imaju kontekstualni meni sa osnovnim informacijama. +\n +\n • Poboljšani algoritmi pretrage +\n +\n • Poboljšane opcije praćenja staza u Navigaciji +\n +\n • Rešeni problemi sa uvozom / izvozom podešavanja profila +\n +\n + Poslednja izmena + Ime: Z - A + Ime: A - Z + Ikone za početak i završetak + Hvala vam što ste kupili „Konturne linije“ + Članarina se naplaćuje po izabranom periodu. Otkažite je u AppGallery u bilo kom trenutku. + Uplata će biti naplaćena sa vašeg računa AppGallery pri potvrdi kupovine. +\n +\nČlanarina se automatski obnavlja ukoliko nije otkazana pre datuma obnove. Vaš račun će biti zadužen za period obnove (mesec / tri meseca / godina) samo na datum obnove. +\n +\nČlanarinama možete upravljati i otkazati ih tako što ćete otići u podešavanja aplikacije AppGallery. + Izbegavajte pešačke staze + Izbegavajte pešačke staze + Razvoj + OsmAnd uživo podaci + OsmAnd uživo podaci + Dvofazno usmeravanje za automobilsku navigaciju. + Preračunava samo početni deo rute, korisno za duga putovanja. + Razvoj matičnog javnog prevoza + Prebacite se na Java (bezbedan) proračun rutiranja javnog prevoza + Prijavite se pomoću OAut-a da biste koristili osm uređivačke funkcije + Prijavite se preko OAut-a + Obrišite OpenStritMap OAut token + Odjavljen + Datoteka je već uvezena u OsmAnd + Koristite dvofazni algoritam usmeravanja A* + Grafikon + %1$s podaci dostupni samo na putevima, izračunajte rutu koristeći „Ruta između tačaka“ da biste videli grafike. + Molimo sačekajte. +\nGrafikon će biti dostupan nakon ponovnog izračunavanja putanje. + Lokalne mape + %1$s — %2$s + Razmak + Ameniti + Special + Prevoz + Usluga + Simboli + Sport + Hitna pomoć + Putovanje + Dodajte najmanje dve tačke + Ova podešavanja dodataka su globalna i primenjuju se na sve profile + Prijavite se na OpenStreetMap.org + Prijavite se na OpenStreetMap.org + Prijavite se pomoću OpenStritMap + Pogledajte sve Vaše još uvek neotpremljene izmene ili OSM beleške u %1$s. Otpremljene tačke se ne više prikazuju. + Morate se prijaviti da biste otpremili nove ili izmenjene promene. +\n +\nMožete se prijaviti koristeći bezbedan OAut metod ili koristite svoje korisničko ime i lozinku. + Koristite korisničko ime i lozinku + Nalog + Lozinka + Prijavite se + Tačke putanje + Pokreni + Slika + Snimak + Putanje + Omiljeno + Moja mesta + Dodaj u „Omiljeno“ + Dodaj + Adresa + Dodato + Uređeno + Izbrisano + Stvarajte ili menjajte OSM tačke od interesa, otvorite ili komentarišite OSM beleške i doprinosite snimanjem GPX fajlova. + Stvarajte ili menjajte predmete OSM-a + Oznaka + Radnje + Izmenite pretragu. + GPX fajl sa koordinatama i podacima svih beležaka. + GPX fajl sa koordinatama i podacima izabrane beleške. + Dodatne radnje + Otvara se u + Otvara se u + Zatvara se u + Otvoreno do + Otvoreno od + Sve tačke grupe + Čitati članak + Čitati ceo članak + Bez vremenskog ograničenja + Pokupiti do + ostavljena u + Ovde su: + Bez imena + Oznake tačaka od interesa + Otvara se sutra u + OSM beleške + Svi podaci + Izvezi kao OSM beleške, tačke od interesa ili oba. + Odaberite tip fajla + OSC fajl + GPX fajl + OSC - pogodno za izvoz u OSM. + GPX - pogodno za izvoz u JOSM i druge OSM uređivače. + Prikaži ili sakrij OSM beleške na karti. + Prikaži zatvorene beleške + Pomeri odredište gore i napravi ga + Dodaje prvo stajanje + Dodaje prolazno stajanje + Trenutna + Označi ovo kao tačku polaska + Karta uvezena + Greška pri uvoženju karte + Unesite ime fajla. + Blizu + Prevoz u blizini + Opciono ime tačke + N + S + W + E + DD°MM.MMM′ + DD°MM.MMMM′ + DD.DDDDD° + DD.DDDDDD° + DD°MM′SS″ + Unesite geografsku širinu i dužinu + Unesite geografsku širinu + Unesite geografsku dužinu + Prvo najbliži + Prvo najudaljeniji + Sportovi na brzacima + Grupa izbrisana + Očisti sve prolazne tačke + Ukupno + Dodajte sve prolazne tačke sa putanje, ili odaberite zasebne kategorije. + Nije nađeno ništa: + Prolazne tačke uklonjene sa oznaka karte + Putovanje + Koristi dve cifre za geografsku dužinu + Rezultat + Sadržaji + Istraži + Zabeleženi članci + Čitaj + Pretraga države, grada ili pokrajine + Članak uklonjen + Wikivoyage + Turistički vodiči + Skidaj slike + Obriši istorijat pretraga + Keš slika + Strana dostupna samo dok ste na mreži. Otvoriti je u veb čitaču\? + Knjiga putovanja + Odaberite knjigu putovanja + Samo bežično + Slike iz članaka mogu biti skidane zbog pristupa i van mreže. +\nUvek dostupno iz „Istraži“ → „Opcije“. + Preuzimaj slike + Samo na bežičnoj mreži + Da + Ne + Odaberite pogodne stavke + Kupite jedno od sledećeg da možete da čitate turističke članke van mreže: + Odaberite plan + Otključaj sve OsmAnd funkcionalnosti + Karte izohipsi i reljefa + Vikipedija van mreže + Neograničeno preuzimanja + Wikivoyage van mreže + Kupi - %1$s + Kada se jednom kupi, biće Vam zauvek dostupno. + Jednokratno plaćanje + Kupovina iz aplikacije + Satna ažuriranja karata + Mesečna ažuriranja karata + Vodiči za najzanimljivija mesta na planeti, unutar OsmAnd-a, bez potrebe za internet konekcijom. + Turistički vodiči + Dobrodošli u otvorenu betu + Uzmi neograničeni pristup + Započni uređivanje + Možete i trebali biste da menjate bilo koji članak na Wikivoyage-u, a i nadamo se da hoćete. Delite Vaše znanje, iskustvo, talenat i pažnju. + Turistički vodiči su trenutno bazirani na Wikivoyage-u. Testirajte sve funkcionalnosti besplatno. + Besplatni svetski turistički vodič koji svako može da uređuje. + Preuzmi fajl + Nadogradnja dostupna + Preuzmite Wikivoyage turističke vodiče da biste čitali članke o mestima po celom svetu bez internet konekcije. + Dostupni su novi Wikivoyage podaci, ažurirajte ih da biste uživali. + Plaćeni dodatak + Plaćena aplikacija + Popularna odredišta + OsmAnd tim + Karte koje Vam trebaju + Na osnovu članaka koji ste zabeležili, predloženo je da skinete sledeće karte: + Obnovite članarinu da nastavite da koristite sve ove funkcionalnosti: + Otkazali ste članarinu za OsmAnd uživo + Prikaži slike + Ponovo pokreni aplikaciju + Preuzmi sve + Čitaj Vikipediju van mreže + Kako da otvorim vezu\? + Da biste čitali članke sa Vikipedije i Wikivoyage-a, kupite članarinu na OsmAnd uživo. + Veza će biti otvorena u veb čitaču. + Otvori Vikipedija vezu sa internetom + Stil opšte namene. Gusti gradovi su prikazani jasno. Prikazuje izohipse, putanje, kvalitet podloge, zabrane pristupa, blokade puteva, iscrtavanje puteva po SAC alpskoj skali, objekti za sportove na brzacima. + Stil za turističke ture sa visokim kontrastom i maksimumom detalja. Uključuje sve opcije OsmAnd podrazumevanog stila, pritom prikazujući što je više detalja moguće, konkretno puteve, staze i ostale načine putovanja. Jasna vizuelna razlika između različitih tipova puteva, tako da podseća na turističke atlase. Pogodan za dnevni i noćni režim rada, kao i korišćenje napolju. + Stari „Mapnik“ stil iscrtavanja. Slične boje kao u „Mapnik“ stilu. + Za šetanje, pešačenje, treking i biciklizam u prirodi. Dobra čitljivost u uslovima spoljne osvetljenosti. Dobar kontrast za puteve i prirodne objekte, različiti tipovi putanja, izohipse sa naprednim podešavanjima, više detalja. Opcija integriteta podloge Vam omogućava da razlikujete puteve sa različitim kvalitetima podloga. Nema noćnog režima. + Jednostavan stil za vožnju. Prijatan za oči noću, izohipse, narandžasti putevi sa dobrim kontrastom, zamagljuje manje bitne objekte sa karte. + Za skijanje. Prikazuje staze, ski liftova, kros-kantri staze itd. Zamagljuje manje bitne objekte sa karte. + Za rečno i morsko navođenje. Prikazuje bove, svetionike, rečne i morske puteve, luke, pomorske oznake i izobate. + Za vožnju van puta, baziran na „Topo“ stilu i pogodan za upotrebu sa zelenim satelitskim slikama kao donji sloj. Smanjena debljina glavnog puta, povećana debljina putanja, staza, biciklističkih i drugih puteva. + Za vožnju motornih sanki sa namenskim putevima i stazama. + Prikaži ceo opis + Sakrij ceo opis + Zabeleži + Izmena podrazumevanog stila za bolji kontrast pešačkih i biciklističkih puteva. Koristi stare Mapnik boje. + Nabavite OsmAnd uživo da otključate ove mogućnosti: dnevna ažuriranja karti sa neograničenim brojem skidanja, svi i plaćeni i besplatni dodaci, Vikipedija, Wikivoyage i još mnogo toga. + Izmeni radnje + Pošaljite snimak ekrana ovog obaveštenja na support@osmand.net + Dodali ste %1$s tačke(i). Unesite ime fajl i kliknite „Snimi“. + Snimi kao putanju + Dodaj tačku + Izmeni tačku + Tačka %1$s obrisana + Svet + Pošalji upit pretrage\? + Poslaćemo upit Vaše pretrage: „%1$s“, kao i Vašu lokaciju. +\n +\n Ne sakupljamo lične podatke, samo nam trebaju podaci o pretragama da bismo poboljšavali algoritam pretrage. + Povećaj poluprečnik pretrage na %1$s + Nema rezultata u pretrazi\? +\nJavite nam + Ne mogu da nađem čvor ili put. + Hvala za povratnu informaciju + Odobrite OsmAnd-u pristup lokaciji da nastavite. + Postoje još prevoza na ovoj stanici. + Zadrži odobrene markere na mapi + Markeri dodati kao grupa Omiljenih ili GPIks prolaznih tačaka označeni kao Odobreni ostaće na mapi. Ako grupa nije aktivna, markeri će se izgubiće sa mape. + Povrati + Prvo odaberite grad/naselje/lokalitet + Traži ulicu + Aplikacija treće strane + Izmeni oznaku karte + Obriši oznaku „%s“ sa karte\? + Donacije pomažu finansiranje OSM kartografa. + Period plaćanja: + %1$.2f %2$s + Godišnje obnavljanje + Kvartalno obnavljanje + Mesečno obnavljanje + Trenutna članarina + Uštedite %1$s + %1$.2f %2$s / mesečno + %1$s / mesečno + godišnje + Svaka 3 meseca + Mesečno + Paketi & cenovnik + Omogućio OsmAnd + Članarine + Dolazak u %1$s + Usputna odredišta + Glasovne najave + Odaberite fajl putanje za praćenje + Simuliraj navođenje + Prikaži uz put + Odaberite put koji biste izbegli u navođenju, ili na mapi ili sa liste ispod: + Javni saobraćaj + Izračunavanje puta… + Tačke od interesa (POI) + Put + pešice + presedanja + Usputne tačke + Postavi početnu tačku + Dodaj usputnu stanicu + Postavi odredište + Visina + Širina + Srednje + Dobro + Izvrsno + Presovano + Fini šljunak + Šljunak + Drvo + Metal + Kamen + Kamenčići + Popločano kamenje + Kaldrma + Kaldrma + Beton + Asfaltirano + Asfalt + Sneg + So + Led + Blato + Prašina + Zemlja + Popločano + Trava + Pesak + Neasfaltirano + Odaberi tačku polaska + Dodaj tačku polaska + Dodaj polazište i odredište + %1$d skidanja + Dodaj prolaznu tačku + Navođenje javnim prevozom je trenutno u beta testiranju, očekujte greške i nepravilnosti. + Pročitajte kako OsmAnd izračunava puteve na našem blogu. + Dužina vrednosti „%s“ + Skratite veličinu oznake „%s“ na manje od 255 karaktera. + Peške + Izbegavaj tipove prevoza… + %s režim + Odaberite tipove javnog prevoza koje treba izbegavati: + Izmeni u čemu se meri azimut. + Jedinice za uglove + Miliradijani + Stepeni + Izbegavanje trajekata + Bez trajekata + Izbegavanje podzemne i lake železnice + Bez podzemne + Izbegavanje vozova + Bez vozova + Izbegavanje deljenog taksija + Bez deljenih taksija + Izbegavanje autobusa i trolejbusa + Bez autobusa + Izbegavanje tramvaja + Bez tramvaja + Pošalji dnevnik + Tip prevoza + Izračunaj put pešaka + Pokušajte sa promenom postavki. + Pokušajte navođenje za pešake. + Nažalost, OsmAnd ne može da nađe put koji odgovara Vašim postavkama. + Put pešice je približno %1$s i možda može biti brži nego sa javnim prevozom + Nemoj premeštati + Premesti karte + %1$d fajlova (%2$s) postoje na prethodnoj lokaciji \'%3$s\'. + Ne mogu da kopiram %1$d fajlova (%2$s). + Kopirano %1$d fajlova (%2$s). + Pomereno %1$d fajlova (%2$s). + Spravica za koordinate + Tražim GPS + Aplikativni profili + Odaberite profile vidljive iz aplikacije. + Rutiranje treće strane + Specijalno rutiranje + Proizvoljni profil rutiranja + OsmAnd rutiranje + BRouter (ne treba internet) + Prava linija + Geokodiranje + Avion, paraglajding + Brod, veslanje, jedrenje + Tipovi javnog prevoza + Hodanje, šetanje, trčanje + Planinarski bicikl, moped, konj + Kola, kamion, motor + Odaberite tip navođenja + Bazirajte Vaš proizvoljni profil na jednom od podrazumevanih profila, ovim se određuju osnovne postavke kao što su podrazuemvana vidljivost spravica ili jedinice brzine i daljine. Ovo su podrazumevani aplikativni profili, zajedno sa primerima proizvoljnih profila kojima se može proširiti: + Odaberite početni profil + Da li ste sigurni da želite da obrišete profil „%s” + Obriši profil + Prvo snimite izmene na profilu + Snimite izmene + Ne možete obrisati OsmAnd osnovni profil + Već postoji profil sa ovim imenom + Ime već postoji + Prvo morate uneti ime profila. + Unesite ime profila + Odaberite tip navođenja za novi aplikativni profil + Odaberite tip navođenja + Osnovni profil + Tip: %s + Skijanje + Korisnički režim, izveden iz: %s + Režim: %s + Odaberite ikonicu + Sakrij lenjir kompas + Prikaži lenjir kompas + Skijanje + Skijanje + Možete dodati Vašu sopstvenu izmenjenu verziju fajla routing.xml u ..osmand/routing + Helikopter + Konj + Podzemna + Autobus + Taksi + Tip navođenja + Ime profila + Dozvoli + Ne, hvala + Odaberite koje podatke delite + Privatnost i bezbednost + Pritisnite na „Dozvoli“ ako se slažete sa našom %1$s + Pomozite nam da razumemo popularnost OsmAnd funkcionalnosti. + Pomozite nam da razumemo popularnost karti država i regija. + Definišite koje podatke dozvoljavate OsmAnd-u da deli. + Preuzeti podaci + Posećeni ekrani + Preuzete karte + Odaberite koje tipove podataka želite da delite: + Dozvoli OsmAnd aplikaciji da sakuplja i obrađuje anonimne podatke o korišćenju aplikacije. Ne sakupljamo podatke o nijednoj lokaciji na karti koju posetite ili pogledate. +\n +\nPromenite Vaš izbor u svakom trenutku sa ’Postavke’ → ’Privatnost i Bezbednost’. + Pomozite nam da poboljšamo OsmAnd + Politika privatnosti + Oceni + Podeliti Vaše iskustvo sa nama i oceniti naš rad na Google Play prodavnici. + Ikonica + Magenta + OsmAnd servis preuzimanja + Pritisnite ponovo da promenite orijentaciju karte + Poslednje izvršavanje aplikacije nije uspelo. Pomozite nam da poboljšamo OsmAnd tako što ćete podeliti poruku o grešci sa nama. + Rušenje + Novi profil + Postavi min/maks brzinu + Promeni podešavanja podrazumevane brzine + Podrazumevana brzina + Maks. brzina + Min. brzina + Skuter + Monocikl + Lično prevozno sredstvo + Horizontalna preciznost: %s + Horizontalna preciznost: %1$s, vertikalna: %2$s + • Aplikativni profili: kreiranje proizvoljnih profila za sopstvene potrebe, sa proizvoljnom ikonicom i bojom +\n +\n • Postavi proizvoljne min/maks brzine na podrazumevane profile +\n +\n • Dodata spravica za trenutne koordinate +\n +\n • Dodata opcija da se prikazuje kompas i lenjir za prečnik na karti +\n +\n • Popravljeno pozadinsko logovanje putanja +\n +\n • Unapređeno pozadinsko preuzimanje karata +\n +\n • Vraćena opcija \'Uključivanje ekrana\' +\n +\n • Popravljen izbor jezika na Vikipediji +\n +\n • Ispravljeno ponašanje dugmeta za kompas prilikom navođenja +\n +\n • Ostale ispravke grešaka +\n +\n + NLO + Broj izmena + Odaberite gornju granicu broja izmena + Odaberite postavke navođenja za profil + Odaberite opcije ekrana za profil + Odaberite opcije mape za profil + Profil zadržava svoja podešavanja + Podesi profil + Van puta + Mahanjem rukom preko ekrana uključiće te ekran. + Koristi senzor blizine + Podesite koliko dugo ekran treba da bude upaljen. + Probudi se kod skretanja + Putanja %s sačuvana + Otvori putanju + %s je sačuvan + Čvrstina podloge + Mekana + Uglavnom mekana + Uglavnom čvrsta + Čvrsta (nepopločana) + Čvrsta (popločana) + Snežni i zaleđeni kolovozi + Zaleđeni kolovoz + Snežni kolovoz + Uključite bar jedan aplikativni profil da koristite ovo podešavanje. + Uključi u obzir i privremena ograničenja + Prikaži zone niske emisije zagađenja + Prikaži zone niske emisije zagađenja. Ne utiče na rutiranje. + Kamp prikolica (RV) + Kamper + Spoj razmake + Podrazumevano + Kamion dostave + Vagon + %1$s • Uštedi %2$s + Poništi članarinu + onda %1$s + Omogućava zapis položaja ostavljenog vozila i koliko je vremena preostalo. +\n I vreme i položaj su vidljivi na komandnoj tabli, a i kao spravica na karti. Može se dodati alarm na Android kalendaru. + Položaj parkiranja + Pravite audio/video beleške za vreme putovanja, ili kroz dugme na karti ili kroz kontekstni meni položaja. + Zabeleške zvuka i snimaka + Uključivanjem ovog pogleda se menja stil mape na „Turistički pregled“, koji je specijalni visokodetaljni pregled za turiste i profesionalne vozače. +\n +\nOvaj pogled nudi, na svakom nivou zuma, najveću količinu turističkih detalja na podacima karte (posebno puteve, putanje, staze i znakove za orijentaciju). +\n +\nTakođe i jasno predstavlja sve vrste puteva po boji, tako da nikad ne bude zabune, što je korisno kada se npr. voze veća vozila. +\n +\nA nudi se i posebne turističke opcije kao što su prikaz biciklističkih ili planinskih ruta. +\n +\nNije potrebno skidanje posebne karte, pogled se pravi od standardnih karti. +\n +\nOvaj pogled se može isključiti tako što se ili ovde deaktivira, ili tako što se, po želji, promeni \"Stil karte\" pod \"Podesi kartu\". + Turistički pregled karte + Ovaj dodatak omogućava prikaz sloja izohipsi i reljefa iznad standardnih OsmAnd karti. Ovu funkcionalnost će najviše znati da cene sportisti, planinari, trekeri i svako koga zanima reljefna struktura predela (primetite da su izohipse i reljefni podaci odvojeni, posebna skidanja za njih su dostupna kada se ovaj dodatak aktivira.) +\n +\nGlobalni podaci (između 70° severno i 70° južno) se bazirani na SRTM (Shuttle Radar Topography Mission) i ASTER (Advanced Spaceborne Thermal Emission and Reflection Radiometer), instrumentima za slikanje na vodećem NASA-inom satelitu Terra. ASTER je zajednički napor NASA-e, japanskog ministarstva ekonomije, trgovine i industrije (METI), i japanskog svemirskog sistema (J-spacesystems). + Izohipse + Ovaj dodatak omogućava prikaz sloja izohipsi i reljefa iznad standardnih OsmAnd karti. Ovu funkcionalnost će najviše znati da cene sportisti, planinari, trekeri i svako koga zanima reljefna struktura predela. +\n +\nGlobalni podaci (između 70° severno i 70° južno) se bazirani na SRTM (Shuttle Radar Topography Mission) i ASTER (Advanced Spaceborne Thermal Emission and Reflection Radiometer), instrumentima za slikanje na vodećem NASA-inom satelitu Terra. ASTER je zajednički napor NASA-e, japanskog ministarstva ekonomije, trgovine i industrije (METI), i japanskog svemirskog sistema (J-spacesystems). + OsmAnd dodatak za linije izohipsi van mreže + Dodatak za izohipse + Ovaj dodatak aktivira funkcionalnost snimanja i čuvanja Vaših putanja kada se pritisne GPX spravica za beleženje na karti, a može i da automatski beleži sve Vaše puteve kojima ste navođeni u GPX fajl. +\n +\nSnimljene putanje se mogu deliti sa prijateljima ili slati na OSM. Sportisti mogu da koriste snimljene putanje da prate svoje treninge. Direktno u OsmAnd-u može da se radi osnovna analiza putanja, kao što su računanje brzine kruga, prosečna brzina itd. Naravno, putanje se dalje mogu analizirati u drugim specijalnim alatima za analizu. + Snimanje putovanja + Pristupite različitim tipovima mrežnih (takozvanim sličicama ili rasterskim) karata, od predefinisanih OSM sličica (kao što je Mapnik) do satelitskih slika i slojevima sa uskom specijalizacijom, kao što su meteorološke, klimatske, geološke karte, osenčeni slojevi, itd. +\n +\nSvaka od ovih mapa može biti korišćena ili kao glavna (osnovna) mapa koja se prikazuje, ili kao sloj iznad ili ispod osnovne karte. Određeni elementi OsmAnd vektorskih karata se mogu sakriti preko „Podesi kartu“ menija. +\n +\nPreuzmite karte sa sličicama direktno sa intereneta, ili ih pripremite za oflajn upotrebu (ručno prekopirati u OsmAnd fasciklu sa podacima), i to kao SQLite baza podataka koju može da napravi veliki broj alata treće strane. + Mrežne karte + Tačke putanje + Odsečci putanje + %1$s prvih %2$s + %1$s prvih %2$s + Nabavite %1$d %2$s za %3$s niže. + Besplatno + Tri meseca + Godina + Godine + Godina + Meseci + Meseca + Mesec + Nedelja + Nedelje + Nedelja + Dana + Dana + Dan + Parametri puta + Konfiguriši parametre puta + Upozorenja na ekranu + Glasovna obaveštenja + Instrukcije i obaveštenja prilikom navođenja + Glasovna obaveštenja su dešavaju samo za vreme navođenja. + Parametri vozika + Težina, visina, dužina, brzina + Ostalo + Karta za vreme navođenja + Karta za vreme navođenja + Uključivanje ekrana + Kopiraj iz drugog profila + Osmand postavke + Utiče na celu aplikaciju + Upravljaj aplikativnim profilima… + Napravi, uvezi, izmeni profile + Resetuj na podrazumevano + Jezik i ulaz + Upozorenja se prikazuju u donjem levom uglu ekrana za vreme navođenja. + Tema aplikacije, jedinice, regija + Podesi navođenje + Instalirani dodaci + Izgled karte + Izgled karte + Merne jedinice & formatiranja + Tajmaut posle buđenja + Postavke za kreiranje puta u označenom profilu „%1$s”. + Prikaži kartu na zaključanom ekranu za vreme navođenja. + Analitike + Poruka po otvaranju + Primeni na sve profile + Primeni samo na „%1$s” + Odbaci promenu + Promeni postavke + Ova postavka je podrazumevano odabrana za profile: %s + Označeni format će biti primenjen kroz celu aplikaciju. + Otvoreni kod lokacije (OLC) + MGRS + UTM Standard + Primer + OsmAnd koristi MGRS, koji je sličan UTM NATO formatu. + OsmAnd koristi UTM Standard format koji je sličan, ali nije istovetan kao UTM NATO format. + Postavke za profil: + Konfiguriši profil + Zameni profil + Aplikativni profil promenjen na „%s“ + Logcat bafer + Postavke dodataka + Podrazumevano + Preuzmi detaljnu kartu mesta %s, da vidite ovu oblast. + Težina staze + Nedefinisano + Ekstremna + Slobodna vožnja + Ekspertska + Napredna + Srednja + Laka + Početnička + Tip staze + Nordijska + Nizbrdo + Skitura + PisteVeza + Pešačenje + Sanke + Saonice + Zimski park + Promeni fasciklu skladišta + Interno skladište za OsmAnd (skriveno od korisnika i drugih aplikacija). + Premesti na novo odredište + Promeni OsmAnd fasciklu sa podacima\? + Umetni putanju do fascikle sa OsmAnd podacima + Fascikla… + Unesite putanju do fascikle + %1$s GB slobodno (od %2$s GB) + %1$s • %2$s + Premesti OsmAnd fajlove podataka na novo odredište\? +\n%1$s > %2$s + Izbegavaj određene puteve i tipove puteva + Veza + Vazdušni put + Uporedno + %1$s kB + %1$s MB + %1$s GB + %1$s TB + Karte + Sličice + Korišćenje OsmAnd-a + Izračunaj + Snimaj staze u podfascikle po danu snimanja (npr. 2018-01-01). + Snimaj staze u dnevnim fasciklama + Snimaj staze u \'rec\' fascikli + Staze mogu biti skladištene u \'rec\' fascikli, ili u mesečnim ili dnevnim fasciklama. + Skladišni direktorijum staza + Da li ste sigurni da želite da ažurirate sve (%1$d) karte\? + Ažuriraj sve karte + Prednost neasfaltiranim putevima. + Prednost neasfaltiranim putevima + Izohipse i reljef + Iskorišćeno %1$s kB + Iskorišćeno %1$s MB + Iskorišćeno %1$s GB + Iskorišćeno %1$s TB + • Ažurirane postavke aplikacija i profila. Zgodno ređanje postavki po njihovom tipu, i mogućnosti da menjaju svaki profil +\n +\n • Novi dijalog za preuzimanje karti koji preporučuje karte za preuzimanje dok se razgleda karta +\n +\n • Popravke za tamnu temu +\n +\n • Ispravljeno nekoliko grešaka u navigaciji svuda po svetu +\n +\n • Ažurirana osnovna karta sa detaljnijom mrežom puteva +\n +\n • Ppravljene poplavljene oblasti svuda po svetu +\n +\n • Ski navigacija: dodati profili nadmorske visine i kompleksnost staza prilikom gledanja detalja staza +\n +\n • Ostale ispravke grešaka +\n +\n + Preferira neasfaltirane puteve ispred asfaltriranih za rutiranje. + Preferiraj neasfaltirane puteve + Deljene + Možete da primenite ovu izmenu na sve ili na samo trenutno odabranom profilu. + Dugme koje prikazuje ili sakriva reljef na karti. + Dugme koje prikazuje ili sakriva linije izohipsi na karti. + OSM izmene + Ne mogu da pokrenem motor za sintetizovanje glasa. + %1$s uvezen. + %1$s greška uvoza: %2$s + Dodajte profil tako što otvorite njegov fajl u OsmAnd-u. + Uvoz profila + Ne mogu da izvezem profil. + ’%1$s’ već postoji. Prebrisati ga\? + OsmAnd profil: %1$s + Izvezi profil + Početna tačka + Zameni %1$s i %2$s + Belo + Procenjuje vreme stizanja kod nepoznatih tipova puteva, ograničava brzinu na svim putevima (može da promeni rutu) + Prazno ime datoteke + Staza sačuvana + Povrati + Očisti %1$s\? + Novi dodatak dodat + Isključi + Profili dodati od strane dodatka + Dodati profili + Ove karte su potrebne za rad ovog dodatka. + Predložene karte + Kontroliše iskačuće prozore, dijaloge i obaveštenja. + Dijalozi i obaveštenja + Dijalog preuzimanja karte + Spoji segmente + Prikaži čvorove mreže biciklističkih puteva + Čvorovi mreže + Izabrani profil + Izmeni listu profila + Ikonica, boja i ime + Izgled profila + Sačuvaj zaglavlje svakoj tački praćenja prilikom snimanja. + Uključi zaglavlje + Dodaj novi profil ’%1$s’\? + Lični + %1$s, %2$s + %1$s • %2$s + ’Tip navođenja’ određuje kako se put izračunava. + Izmeni profile + Podrazumevani OsmAnd profile ne mogu da se izbrišu, ali mogu da se isključe (na prethodnom ekranu), ili da se stave na dno. + Preuzimam %s + Za pustinje i ostale retko naseljene oblasti. Detaljnije. + Debelo + Odaberite boju + Glavni profil + Izbrisani profili će zauvek biti izbrisani kada kliknete ’Primeni’. + Pozicija ikonice pri mirovanje + Ikonica položaja dok se krećete + Resetuj sva podešavanja profila\? + Resetuj sva podešavanja profila na stanje posle instalacije. + Klikom na %1$s odbacuju se sve vaše promene. + %1$s: %2$s + %1$s %2$s + Ikonica koja se prikazuje u mirovanju. + Ikonica koja se prikazuje za vreme navođenja ili pomeranja. + OSM + OSM uređivanje + Korisničko ime i lozinka + Objavi + Preračunavanje puta + Foto beleške + Video beleške + Vaše OSM beleške su u %1$s. + Vaše snimljene staze su u %1$s, ili u OsmAnd fascikli. + Tačnost beleženja + Praćenje preko interneta + Dozvoljava deljenje trenutne pozicije koristeći snimanje puta. + Odaberite ikonu, boju i ime + Prijava, lozinka, uređivanje van mreže + Veličina slike, audio i video kvalitet + Navođenje, tačnost beleženja + Uvezi profil + Uvezi fajl za rutiranje + Uvezi iz fajla + Provera identiteta uspela + Zvuk školjcanja fotoaparata + Koristi sistemsku aplikaciju + Izdeljivanje snimanja + Resetuj postavke dodataka na podrazumevane vrednosti + Minimalni pomeraj + Minimalna preciznost + Minimalna brzina + Obaveštenje + Odaberite veb adreus sa sintaksom za parametre: lat={0}, lon={1}, timestamp={2}, hdop={3}, altitude={4}, speed={5}, bearing={6}. + Kapacitet + t + + Posao + Dodaj posao + Dodaj kuću + Prethodni put + Prvo postavite odredište + Dugme koje prikazuje ili sakriva odabrane putanje sa karte. + Prikazane putanje + Prikaži još + Zameni + Ukrcavanje na stajanju + Izlaz na + Tipovi puteva + Korak-po-korak + Do %1$s + Doba dana + OsmAnd uživo javni prevoz + Omogući javni prevoz na OsmAnd uživo izmenama. + Kvalitet 5 + Kvalitet 4 + Kvalitet 3 + Kvalitet 2 + Kvalitet 1 + Nedefinisano + Biciklistička staza + Staza + Stepenice + Konjička staza + Staza + Pešačka staza + Pristupni put + Ulica + Put + Državni put + Auto-put + Neprohodno + Preužasno + Užasno + Veoma loše + Loše + Upravljajte članarinom + Postoji problem sa Vašom članarinom. Kliknite na dugme da biste otvorili podešavanja pretplate za Gugle Plej i da biste popravili način plaćanja. + Članarina za OsmAnd uživo je istekla + Članarina na OsmAnd uživo je pauzirana + Članarina na OsmAnd uživo je na čekanju + Istorija markera + Pošaljite GPKS datoteku na OpenStritMap + Unesite oznake odvojene zarezom. + „Javno“ znači da je trag javno prikazan u vašim GPS tragovima i na javnim GPS tragovima, kao i na javnom spisku tragova sa vremenskim oznakama u sirovom obliku. Podaci koji se prikazuju putem API-ja ne upućuju na vašu stranicu tragova. Vremenske oznake praćenja nisu dostupne putem javnog GPS API-ja i tačke praćenja nisu hronološki poređane. + „Privatno“ znači da se trag ne pojavljuje ni na jednoj javnoj listi, ali su tačke praćenja u njemu u nehronološkom redosledu dostupne putem javnog GPS API-ja bez vremenskih oznaka. + „Moguće je identifikovati“ znači da će se trag javno prikazati u Vašim GPS tragovima i u javnim spiskovima GPS tragova, tj. drugi korisnici će moći da preuzmu neobrađeni trag i povežu ga sa Vašim korisničkim imenom. Javni podaci o vremenskim tačkama traga iz GPS API-ja koji se serviraju putem API-ja za tačke praćenja imaće referencu na vašu originalnu stranicu praćenja. + „Sledljivo“ znači da se trag ne prikazuje nigde na javnim listama, ali obrađene tačke praćenja sa vremenskim oznakama u njima (koje ne mogu biti direktno povezane sa vama) idu kroz preuzimanja sa javnog GPS API-ja. + Zatvori OSM belešku + Komentar OSM napomene + Možete se prijaviti koristeći bezbedan OAut metod ili koristiti svoje korisničko ime i lozinku. + Dodaj fotografiju + Registrujte se na +\nOpenPlaceReviews.org + Fotografije pruža projekat otvorenih podataka OpenPlaceReviews.org. Da biste postavili svoje fotografije, morate se prijaviti na veb stranicu. + Napravite novi nalog + Već imam + Pretraga + Kajak + Motorni čamac + Nije moguće otpremiti sliku, pokušajte ponovo kasnije + Izaberite sliku + Resursi + Približna veličina datoteke + Izaberite podatke za izvoz u datoteku. + Potrebno za uvoz + Uređaj ima slobodno samo %1$s. Oslobodite malo prostora ili poništite odabir nekih predmeta za izvoz. + Nema dovoljno prostora + Izaberite grupe koje će biti uvezene. + Izaberite stavke koje će biti uvezene. + Dodaj u Mapillary + Dodaj u OpenPlaceReviews + Pređite na upotrebu dev.openstreetmap.org umesto na openstreetmap.org da biste testirali otpremanje OSM beleške / POI / GPKS. + Upotrebi dev.openstreetmap.org + OsmAnd prikazuje fotografije iz nekoliko izvora: +\nOpenPlaceReviews - fotografije tačaka od interesa; +\nMapillary - slike na nivou ulice; +\nVeb / Vikimedija - fotografije tačaka od interesa navedene u OpenStreetMap podacima. + %1$s * %2$s + Laka letelica + Spoji segmente + Podeli pre + Posle podeli + Dodajte novi segment + OsmAnd profil + Korisnički profil + • Dodata je opcija za izvoz i uvoz svih podataka, uključujući podešavanja, resurse, moja mesta +\n +\n • Planiranje rute: grafikoni za segmente sa rutom, i dodata je mogućnost kreiranja i uređivanja višestrukih segmenata staza +\n +\n • Dodan je metod autentifikacije OAut za OpenStritMap, poboljšan korisnički interfejs OSM dijaloga +\n +\n • Podrška za prilagođene boje za omiljene i prolazne tačke putanja +\n +\n + Obrni sve tačke + Odaberite profil koje će se koristiti po pokretanju aplikacije. + Poslednje korišćeno + Preferira planinske pešačke puteve + Preferiraj planinske pešačke puteve + Dozvoljava potoke i odvoje + Dozvoli potoke i odvode + Dozvoljava vodene tokove koji nekad presuše + Dozvoli nestalne vodene tokove + Dodaj mrežni usmerivač + Izmeni mrežni usmerivač + Podvrsta + Vozilo + Ključ API-ja + Adresa servera + Unesite parametar + Zadrži je praznom ako nije + Adresa sa svim parametrima će izgledati ovako: + Testiraj izračunavanje puta + Vožnja automobila + Peške + Bicikl + Automobil + Kopiraj adresu + Mrežni usmerivač + Mrežni usmerivači + Fascikle + Odaberite fasciklu + Odaberite fasciklu ili dodajte novu + Prazno + Analiziraj intervale podele + Otpremi na OpenStreetMap + Uredi putanju + Preimenuj putanju + Izmeni fasciklu + sek + Prolaženje + Prilazak + Priprema unapred + Priprema + Van puta + Dolazak na odredište + Skretanje + Intervali vremena i udaljenosti + Vremena objava različitih glasovnih navođenja zavise od tipa objave, kao i trenutne i podrazumevane brzine navođenja. + Vreme objave + Započni snimanje + Prikaži putanju na karti + Invalidska kolica + Planinarenje + Pešačenje + Eketrični biciklizam + Planinski biciklizam + Drumski biciklizam + Standardni biciklizam + Teretni kamion + Kamionet + Kamion + Skuter + Trkački bicikl + MTB + Serverska greška: %1$s + Ime već postoji + Obriši ovaj mrežni usmerivač\? + Pročitaj u celosti + Izmeni opis + Obriši prolaznu tačku + Kopiraj u oznake karte + Kopiraj u omiljene + Otpremam + Otpremanje završeno + Otprema se %1$d od %2$d + Odaberite segmente + %1$s sadrži više od jednog segmenta, morate odabrati traženi deo za navođenje. + Segment %1$d + Otpremljeno %1$d od %2$d + Odaberite izmene za otpremanje + Reljef / Nagib / Konturne linije + OpenPlaceReviews je projakat pokretan od strane zajednice o javnim mestima, kao što su restorani, hoteli, prolazne tačke. Projekat sakuplja javne informacije o njima, kao što su slike, recenzije, veze ka drugim mestima kao OpenStreetMap ili Vikipedija. +\n +\nSvi podaci na OpenPlaceReview su otvoreni i dostupni svima: http://openplacereviews.org/data. +\n +\nMožete pročitati više na: http://openplacereviews.org + OpenPlaceReviews + Koristi test.openplacereviews.org + Prijavite se za OpenPlaceReviews + Voda + Zima + Motorne sanke + Jahanje + Trkanje + Planinska bicikla + Bicikla + Planinarenje + Trčanje + Pešačenje + Vožnja van puta + Motor + Automobil + Koristi ograničenja na putu trenutno aktivna na karti + Optimizovani kraći put (ušteda energije) + Odaberite razlog putovanja da dobijete kraći, brži ili sigurniji put + Ikonica trenutne lokacije će biti prilepljena za trenutno navođeni put + Ne rotiraj kartu ako je brzina ispod donje granice + Restart + Sve regije + Obriši %1$d fajlova\? + Zaustavi bez snimanja + Sačuvaj i zaustavi snimanje + Snimanje putanje zaustavljeno + Da li ste sigurni da želite da zaustavite snimanje\? +\nSve nesačuvani podaci će biti izgubljeni. + Pauzirano + Potreban je restart aplikacije da se neka podešavanja primene. + Rutiranje može da zaobiđe velike uzbrdice. + Prekidač da na karti pokaže ili sakrije spravicu sa koordinatama. + Udaljenost po kliktanju + Poslednje OpenStreetMap ažuriranje dostupno: + Ažurirano: %s + Poslednje vreme provere: %s + Učestalost ažuriranja + Provera ažuriranja karte će biti svakih nedelju dana. Sledeće vreme %1$s za %2$s. + Provera ažuriranja karte će biti svakog dana. Sledeće vreme %1$s za %2$s. + Provera ažuriranja karte će biti na svakih sat vremena. Sledeće vreme %1$s za %2$s. + Obriši ažuriranja + Da li ste sigurni da želite da obrišete %s ažuriranja uživo\? + Kupovine + Odaberite kategoriju ili dodajte novu + Snimanje će biti nastavljeno. + Kopiraj ime tačke od interesa + Prikaži/sakrij + Interval + Sakrij rezervate prirode, zaštićena područja i granice nacionalnih parkova + Prirodne granice + Putanja ne sadrži podatke o nadmorskoj visini. + Putanja ne sadrži podatke o brzini. + Odaberite drugi tip bojenja. + Vremenski interval beleženja na koji će OsmAnd da pita za podatke o trenutnoj poziciji. + Sačuvaj i nastavi + Svi nesačuvani podaci će biti izgubljeni. + Prikaži početni dijalog + Ako se isključi, snimanje će početi odmah posle kliktanja na spravicu ili na stavku menija, preskakajući dijalog potvrde. + Prilagodi liniju rute + Linija rute + Linija rute će koristiti %1$s odabran na označenom stilu karte: %2$s. + Odaberite boju za režim karte: %1$s. + Nemate nijednu kupovinu + Novi uređaj / novi nalog + Ako imate ikakvih pitanja, kontaktirajte nas na %1$s. + Ako se Vaša kupovina ne pojavi ovde, kliknite na „%1$s” ili kontaktirajte našu podršku. + Kontaktirajte podršku + Rešavanje problema + Pratite ovu vezu ako imate ikakvih problema sa kupovinom. + OsmAnd uživo + Godišnja članarina + Mesečna članarina + Tromesečna članarina + Sledeći datum naplate: %1$s + Otkazano + Obnovi članarinu + U grejs periodu + Na čekanju + Isteklo + • Ažuriranja OsmAnd uživo su premeštena u „Preuzimanja > Ažuriranja” +\n +\n • Putanje se sada mogu obojiti po visini, brzini ili nagibu. +\n +\n • Dodata opcija da se izmeni izgled linije koja se prikazuje prilikom navođenja +\n +\n • Ažuriran dijalog za „Snimanje puta” +\n +\n + Ažuriraj sve karte dodate na %1$s\? + Broj izlaza + Objavi kada se premaši + Korisnički poeni + Izlaz + \ No newline at end of file diff --git a/OsmAnd/res/values-da/strings.xml b/OsmAnd/res/values-da/strings.xml index 7a6398afca..4272306e50 100644 --- a/OsmAnd/res/values-da/strings.xml +++ b/OsmAnd/res/values-da/strings.xml @@ -3837,4 +3837,8 @@ Sporet indeholder ikke højdedata. Sporet indeholder ikke hastighedsdata. Vælg en anden type farvelægning. + Udgangsnummer + Meddelelse ved overskridelse + Bruger points + Resultat \ No newline at end of file diff --git a/OsmAnd/res/values-de/phrases.xml b/OsmAnd/res/values-de/phrases.xml index 8073e59ecb..433d3766aa 100644 --- a/OsmAnd/res/values-de/phrases.xml +++ b/OsmAnd/res/values-de/phrases.xml @@ -3926,4 +3926,6 @@ Karate Diplomatische Vertretung Art der Bucht + Plateau + Freizeitclub \ No newline at end of file diff --git a/OsmAnd/res/values-de/strings.xml b/OsmAnd/res/values-de/strings.xml index dae511a7f4..c9c4092d52 100644 --- a/OsmAnd/res/values-de/strings.xml +++ b/OsmAnd/res/values-de/strings.xml @@ -3856,7 +3856,7 @@ \nSie können sich mit der sicheren OAuth-Methode anmelden oder Ihren Benutzernamen und Ihr Passwort verwenden. Benutzername und Passwort verwenden Konto - Benutzername + Anmelden Historie der Marker GPX-Datei an OpenStreetMap senden Geben Sie durch Komma getrennte Tags ein. @@ -4057,7 +4057,11 @@ Routenlinie anpassen Routenlinie Legen Sie die Farbe für den Kartenmodus fest: %1$s. - • OsmAnd Live-Updates nach \"Downloads > Updates\" verschoben + • Option zum Herunterladen von Höhenlinien in Fuß zugefügt +\n +\n• Routenplanung im Querformat: Registerkarten zum Umschalten zwischen Punkten oder Diagrammen hinzugefügt +\n +\n• OsmAnd Live-Updates nach \"Downloads > Updates\" verschoben \n \n• Tracks können nun nach Höhe, Geschwindigkeit oder Steigung eingefärbt werden. \n @@ -4089,4 +4093,8 @@ Meldung bei Überschreitung Anwenderpunkte Leistung + Fuß + Einheit der Höhenlinien + OsmAnd liefert Höhenlinien-Daten in Metern und Fuß. Sie müssen die Datei erneut herunterladen, um das Format zu ändern. + Bitte wählen Sie das gewünschte Format. Sie müssen die Datei erneut herunterladen, um das Format zu ändern. \ No newline at end of file diff --git a/OsmAnd/res/values-eo/phrases.xml b/OsmAnd/res/values-eo/phrases.xml index 433722b7f3..d0982ff069 100644 --- a/OsmAnd/res/values-eo/phrases.xml +++ b/OsmAnd/res/values-eo/phrases.xml @@ -3926,4 +3926,6 @@ Korboj Tipo de golfo Diplomatia oficejo + Altebenaĵo + Socia klubejo \ No newline at end of file diff --git a/OsmAnd/res/values-eo/strings.xml b/OsmAnd/res/values-eo/strings.xml index fa137e6afc..d8d6c27220 100644 --- a/OsmAnd/res/values-eo/strings.xml +++ b/OsmAnd/res/values-eo/strings.xml @@ -4054,7 +4054,11 @@ Alĝustigi linion de kurso Linio de kurso Por la linio de kurso estos uzata %1$s difinita por la map‑stilo: %2$s. - • ĝisdatigoj OsmAnd Live movitaj al “elŝutoj” → “ĝisdatigoj” + • eblo elŝuti nivelkurbojn en futoj +\n +\n• horizontala vido por “plani kurson”: aldonitaj langetoj por baskuli inter punktoj kaj diagramoj +\n +\n• ĝisdatigoj OsmAnd Live movitaj al “elŝutoj” → “ĝisdatigoj” \n \n• eblo kolorigi spurojn laŭ altitudo, rapido aŭ dekliveco \n @@ -4085,4 +4089,8 @@ Numero de elirejo Poentoj de uzanto Eligo + futoj + Unuo por nivelkurboj + OsmAnd liveras nivelkurbojn en metroj kaj en futoj. Ve devos reelŝuti la dosieron por ŝanĝi la formon. + Elektu la deziratan formon. Vi devos reelŝuti la dosieron por ŝanĝi la formon. \ No newline at end of file diff --git a/OsmAnd/res/values-es-rAR/phrases.xml b/OsmAnd/res/values-es-rAR/phrases.xml index 0505f3549f..bcbc9d8b88 100644 --- a/OsmAnd/res/values-es-rAR/phrases.xml +++ b/OsmAnd/res/values-es-rAR/phrases.xml @@ -3926,4 +3926,6 @@ Aros Oficina diplomática Tipo de bahía + Meseta (altiplano) + Club social \ No newline at end of file diff --git a/OsmAnd/res/values-es-rAR/strings.xml b/OsmAnd/res/values-es-rAR/strings.xml index 193189aa46..a994d3f430 100644 --- a/OsmAnd/res/values-es-rAR/strings.xml +++ b/OsmAnd/res/values-es-rAR/strings.xml @@ -3126,7 +3126,7 @@ Cancelar suscripción %1$s • Ahorra %2$s Ajustes para el perfil: - OsmAnd utiliza el formato UTM Estándar, similar pero no idéntico al formato UTM NATO. + OsmAnd usa el formato UTM Estándar, similar pero no idéntico al formato UTM de la OTAN. Ejemplo UTM Estándar Código de Ubicación Abierto diff --git a/OsmAnd/res/values-es-rUS/strings.xml b/OsmAnd/res/values-es-rUS/strings.xml index 80780e72e8..3fb411ea12 100644 --- a/OsmAnd/res/values-es-rUS/strings.xml +++ b/OsmAnd/res/values-es-rUS/strings.xml @@ -3125,7 +3125,7 @@ Cancelar suscripción %1$s • Ahorra %2$s Ajustes para el perfil: - OsmAnd utiliza el formato UTM Estándar, similar pero no idéntico al formato UTM NATO. + OsmAnd usa el formato UTM Estándar, similar pero no idéntico al formato UTM de la OTAN. Ejemplo UTM Estándar Código de Ubicación Abierto diff --git a/OsmAnd/res/values-fa/phrases.xml b/OsmAnd/res/values-fa/phrases.xml index 7dc383d9c1..63d5c094fa 100644 --- a/OsmAnd/res/values-fa/phrases.xml +++ b/OsmAnd/res/values-fa/phrases.xml @@ -1460,4 +1460,5 @@ بانوان بانوان لباس کار + باغداری گلخانه‌ای \ No newline at end of file diff --git a/OsmAnd/res/values-fr/phrases.xml b/OsmAnd/res/values-fr/phrases.xml index 0164a38387..d73665cdf6 100644 --- a/OsmAnd/res/values-fr/phrases.xml +++ b/OsmAnd/res/values-fr/phrases.xml @@ -3915,4 +3915,17 @@ Pilates Ju-jitsu Karaté + Gymnase Zurkhaneh + Plongeon du haut d\'une falaise + Anneaux + Club social + Plateau + Voie express + Satisfait : oui + Emplacement de camping + Arrêt pour la bibliothèque mobile + Bureau diplomatique + Fers à cheval + Polo-vélo + Type de baie \ No newline at end of file diff --git a/OsmAnd/res/values-fr/strings.xml b/OsmAnd/res/values-fr/strings.xml index 22ec8f53c2..466f9571b7 100644 --- a/OsmAnd/res/values-fr/strings.xml +++ b/OsmAnd/res/values-fr/strings.xml @@ -301,8 +301,8 @@ Impossible de lire les données GPX. Cartes vectorielles OSM Recherche de transport pour cet arrêt - Modifier point d\'intérêt - Supprimer point d\'intérêt + Modifier le point d\'intérêt + Supprimer le point d\'intérêt D\'après la boussole Dans la direction du déplacement Aucune rotation (nord toujours en haut) @@ -3275,7 +3275,7 @@ Ajouter le profil \'%1$s\' \? Inclure la direction Inclure la direction de chaque point lors de l\'enregistrement d\'une trace. - Afficher le réseau de nœuds de pistes cyclables + Afficher les itinéraires cyclables du réseau de nœuds Nœuds de transport Personnel %1$s • %2$s @@ -4040,9 +4040,13 @@ Toutes les données non enregistrées seront perdues. Afficher la boîte de dialogue de démarrage Si désactivé, l\'enregistrement débutera dès appui sur le gadget ou dans le menu (sans demande de confirmation). - - Les mises à jour d\'OsmAnd Live ont été déplacées vers \"Téléchargements > Mises à jour\" + - Ajout d\'une option pour télécharger les Courbes de niveau en pieds \n -\n - Les traces peuvent maintenant être colorées par altitude, vitesse ou pente. +\n- Planification d\'itinéraires en paysage : ajout d’onglets pour basculer entre Points et Graphs +\n +\n- Les mises à jour d\'OsmAnd Live ont été déplacées vers \"Téléchargements > Mises à jour\" +\n +\n - Les traces peuvent maintenant être colorées par altitude, vitesse ou pente \n \n - Ajout d\'une option pour modifier l\'apparence de la ligne d\'itinéraire en navigation \n @@ -4075,4 +4079,8 @@ Numéro de sortie Annoncer en cas de dépassement Sortie + OsmAnd fournit des données sur les courbes de niveau en mètres et en pieds. Vous devrez à nouveau télécharger le fichier pour modifier le format. + Veuillez sélectionner le format souhaité. Vous devrez à nouveau télécharger le fichier pour modifier le format. + Format d\'unité pour les courbes de niveau + pieds \ No newline at end of file diff --git a/OsmAnd/res/values-hu/phrases.xml b/OsmAnd/res/values-hu/phrases.xml index 42e9bcd346..9e1280b90c 100644 --- a/OsmAnd/res/values-hu/phrases.xml +++ b/OsmAnd/res/values-hu/phrases.xml @@ -3924,4 +3924,5 @@ Kosárlabdapalánkok Diplomáciai iroda Öböl típusa + Fennsík \ No newline at end of file diff --git a/OsmAnd/res/values-hu/strings.xml b/OsmAnd/res/values-hu/strings.xml index ced108288e..c7cdd9ca08 100644 --- a/OsmAnd/res/values-hu/strings.xml +++ b/OsmAnd/res/values-hu/strings.xml @@ -13,8 +13,8 @@ Súgó Akadálymentes mód Bekapcsolja a fogyatékkal élőknek szóló funkciókat. - Be - Kikapcsolás + Bekapcsolva + Kikapcsolva Android rendszerbeállítások szerint Vissza a menübe Kicsinyít @@ -1318,7 +1318,7 @@ Bővebben… Célpont Egy meglévő elem adatainak megtekintéséhez koppintson rá, inaktiváláshoz vagy törléshez nyomja meg hosszan. Jelenlegi adatok az eszközön (%1$s szabad): - Sebességkorlátozás-tolerancia + Sebességkorlátozás túllépésének tűréshatára Válassza ki a sebességkorlátozás tűréshatárát, amely fölött hangos figyelmeztetést fog kapni. A Kedvenc hely át lett nevezve erre: %1$s, hogy a hangulatjeleket tartalmazó szöveget fájlba lehessen menteni. Útvonal nyomtatása @@ -1470,7 +1470,7 @@ Megjelent napos Térképek letöltése - Néhány országban (Németország, Franciaország, Olaszország és még néhány) tilos a traffipax figyelmeztetés használata! Az OsmAnd nem vállal felelősséget a szabályok megsértéséért. Koppints az Igen gombra, ha jogosult vagy a funkció használatára. + Néhány országban (Németország, Franciaország, Olaszország stb.) tilos traffipax-riasztást használni! Az OsmAnd nem vállal felelősséget a szabályok megsértéséért. Koppintson az Igen gombra, ha jogosult a funkció használatára. A jelzőtáblák és szabályok helyes értelmezéséhez jelölje ki a vezetési régiót: Az OsmAnd lehetővé teszi a térképek és a navigáció offline használatát az egész világon. Jelenlegi útvonal @@ -3583,7 +3583,7 @@ Traffipaxok eltávolítása Jogi Traffipax POI-k - Bizonyos országokban és régiókban a traffipaxra figyelmeztető alkalmazások törvényileg tiltottak. + Bizonyos országokban és régiókban jogszabály tiltja a traffipaxra figyelmeztető alkalmazások használatát. \n \nDöntsön az ön országának törvényei alapján. \n @@ -3592,14 +3592,14 @@ \nVálassza az %2$s lehetőséget, hogy az összes traffipax-szal kapcsolatos adat: riasztások, értesítések, POI-k törlésre kerüljenek az OsmAnd teljes újratelepítéséig. Maradjanak Eltávolít - Bizonyos országokban a traffipax riasztások törvényileg tiltottak. + Bizonyos országokban a törvény tiltja a traffipaxriasztást. Tájolás %1$s törölve A traffipaxadatok végleges törléséhez indítsa újra az alkalmazást. Eltávolítás és Újraindítás Adja meg az útvonalakon a járművekre vonatkozó hosszkorlátozást. Hosszkorlátozás - Az eszköz nem tartalmaz traffipax adatokat. + Az eszköz nem tartalmaz traffipaxadatokat. Az jelenlegi elemek lecserélésre kerülnek a fájlban lévőkre Az importált elemek előtaggal lesznek hozzáadva Az OsmAnd már tartalmaz az importálttal megegyező nevű elemeket. @@ -4044,7 +4044,11 @@ Minden nem mentett adat törlődni fog. Kezdő párbeszéd megjelenítése Ha le van tiltva, akkor a felvétel közvetlenül a widget vagy a menüelem megérintése után elindul, kihagyva a megerősítő párbeszédpanelt. - • Az OsmAnd Live frissítések átköltöztek a „Letöltések> Frissítések” helyre + • Új lehetőség: szinvonalak letöltése (méter mellett) lábban +\n +\n• Útvonaltervezési környezet: hozzáadott fülek a pontok és grafikonok közötti váltáshoz +\n +\n• Az OsmAnd Live frissítések átköltöztek a „Letöltések> Frissítések” helyre \n \n• A nyomvonalak színezhetők magasság, sebesség vagy lejtés szerint. \n @@ -4078,4 +4082,8 @@ Értesítés túllépéskor Felhasználói pontok Teljesítmény + Kérjük, válassza ki a kívánt mértékegységet. A mértékegység módosításához újra le kell töltenie a fájlt. + Az OsmAnd méterben és lábban adja meg a szintvonalak magasságát. A mértékegység módosításához újra le kell töltenie a fájlt. + láb + Szintvonalak mértékegysége \ No newline at end of file diff --git a/OsmAnd/res/values-is/phrases.xml b/OsmAnd/res/values-is/phrases.xml index 2e3ad0b907..25a7bc3d25 100644 --- a/OsmAnd/res/values-is/phrases.xml +++ b/OsmAnd/res/values-is/phrases.xml @@ -3926,4 +3926,6 @@ Karate Sendiskrifstofa Gerð flóa + Háslétta + Félagsstarf \ No newline at end of file diff --git a/OsmAnd/res/values-is/strings.xml b/OsmAnd/res/values-is/strings.xml index 8b0b8e87a5..4621b9ab38 100644 --- a/OsmAnd/res/values-is/strings.xml +++ b/OsmAnd/res/values-is/strings.xml @@ -4052,7 +4052,11 @@ Mörk náttúru Millibil skráninga stillir tímabilið milli þess sem OsmAnd biður um staðsetningargögn. Ef þetta er óvirkt, mun upptaka hefjast strax eftir að ýtt er á hnappinn eða valmyndarfærsluna og staðfestingarglugga er þá sleppt. - • Uppfærslur OsmAnd Live færðar í \"Sótt gögn > Uppfærslur\" + • Bætt við möguleika á að sækja hæðarlínur í fetum +\n +\n• Landslag í leiðaskipulagi: bætt við flipum til að skipta á milli punkta eða grafs +\n +\n• Uppfærslur OsmAnd Live færðar í \"Sótt gögn > Uppfærslur\" \n \n • Ferla er nú hægt að lita eftir hæð, hraða eða halla. \n @@ -4091,4 +4095,8 @@ Tilkynna þegar farið er yfir Punktar notanda %1$s → … + fet + Snið eininga hæðarlína + OsmAnd býður upp á hæðalínugögn í metrum og fetum. Þú þarft að sækja skrána aftur til að breyta sniðinu. + Veldu rétt snið eininga. Þú þarft að sækja skrána aftur til að breyta sniðinu. \ No newline at end of file diff --git a/OsmAnd/res/values-iw/strings.xml b/OsmAnd/res/values-iw/strings.xml index 8fee045082..6a625e3b66 100644 --- a/OsmAnd/res/values-iw/strings.xml +++ b/OsmAnd/res/values-iw/strings.xml @@ -4056,13 +4056,17 @@ להתאים קו מסלול אישית קו מסלול לציין צבע למצב מפה: %1$s. - • העדכונים החיים של OsmAnd הועברו אל „הורדות > עדכונים” + • נוספה אפשרות להוריד קווי מתאר ברגל \n -\n • אפשר לצבוע מסלולים לפי גובה, מהירות או שיפוע. +\n• תכנון מסלול אופקית: נוספו לשוניות למעבר בין נקודות לתרשימים \n -\n • נוספה אפשרות לשנות את מראה קו מסלול הניווט +\n• העדכונים החיים של OsmAnd הועברו אל „הורדות > עדכונים” \n -\n • החלונית „הקלטת מסלול” עודכנה +\n• אפשר לצבוע מסלולים לפי גובה, מהירות או שיפוע. +\n +\n• נוספה אפשרות לשנות את מראה קו מסלול הניווט +\n +\n• החלונית „הקלטת מסלול” עודכנה \n \n OsmAnd חי @@ -4087,4 +4091,8 @@ להכריז בחריגה נקודות משתמש פלט + רגל + תצורת יחידת קווי מתאר + OsmAnd מספק נתוני קווי מתאר במטרים וברגל. יהיה עליך להוריד את הקובץ מחדש כדי לשנות את התצורה. + נא לבחור את התצורה הרצויה. יהיה עליך להוריד את הקובץ מחדש כדי לשנות את התצורה. \ No newline at end of file diff --git a/OsmAnd/res/values-ja/strings.xml b/OsmAnd/res/values-ja/strings.xml index 78c1373194..fdca4a0e3b 100644 --- a/OsmAnd/res/values-ja/strings.xml +++ b/OsmAnd/res/values-ja/strings.xml @@ -1173,7 +1173,7 @@ POIの更新は利用できません お気に入りのグループとして保存 目的地の設定 施設の名称 - ロード中 %1$s … + %1$sをロード中… 現在時刻 経由地点 " @@ -1446,7 +1446,7 @@ POIの更新は利用できません ナビゲーション設定 全般設定 - 有効 + 有効化 無効化 選択済み 選択解除 @@ -2818,9 +2818,9 @@ POIの更新は利用できません サブスクリプション By OsmAnd プランと料金 - 月間 + 月毎 3ヶ月毎 - 年間 + 年毎 %1$s / 月 %1$.2f %2$s / 月 %1$s割引 @@ -3172,7 +3172,7 @@ POIの更新は利用できません 横並び(サイドバイサイド)形式 索道(リフトやロープウェイなど) リフト間接続 - 計算 + 計算する OsmAndの合計使用容量 タイル マップ @@ -3703,9 +3703,9 @@ POIの更新は利用できません \n \n国の法律に基づいて、使用を望むかどうかを決定する必要があります。 \n -\n%1$sを選択すると、スピードカメラに関するアラートと警告機能を使用できます。 +\n『%1$s』を選択すると、スピードカメラに関するアラートと警告機能を使用できます。 \n -\n%2$sを選択すると、スピードカメラに関するすべてのデータ(警告、通知、POI)が、OsmAndの再インストールを行うまで削除されます。 +\n『%2$s』を選択すると、スピードカメラに関するすべてのデータ(警告、通知、POI)が、OsmAndの再インストールを行うまで削除されます。 機能を維持 アンインストール 一部の国では、スピードカメラの事前警告は法律で禁止されています。 @@ -4078,4 +4078,26 @@ POIの更新は利用できません 基本前進のみの車椅子 通常色 逆方向の場合 + 購入済みの品目はありません + 新しい端末 / 新規アカウント + ご不明な点がございましたら、%1$sまでお問い合わせください。 + 購入した品目がここに表示されない場合は、\"%1$s\"をタップするか、サポートチームにお問い合わせください。 + サポート問い合わせ先 + トラブルシューティング + 購入に関して問題が発生した場合は、このリンクを参照してください。 + OsmAnd Live + 次回請求日: %1$s + キャンセル済み + 猶予期間中 + 保留中 + 期限切れ + %1$sに追加された全マップを更新しますか? + ユーザーポイント + 出口番号 + 超過した場合の通知 + 出力 + 年毎のサブスクリプション + 月毎のサブスクリプション + 3ヶ月毎のサブスクリプション + サブスクリプションの更新 \ No newline at end of file diff --git a/OsmAnd/res/values-lv/strings.xml b/OsmAnd/res/values-lv/strings.xml index 27ad892e50..5b04fd8c4b 100644 --- a/OsmAnd/res/values-lv/strings.xml +++ b/OsmAnd/res/values-lv/strings.xml @@ -1802,7 +1802,7 @@ failu(s)? Sākt lietot Kā lejupielādēt kartes un pamatiestatījumu lietošana Navigācija - Ceļa plānošana + Ceļojuma plānošana BUJ Biežāk uzdotie jautājumi Kartes skatīšana @@ -2162,7 +2162,6 @@ Apraksta laukumu: %1$s x %2$s Laiks Maršruta garums Lietot elevācijas datus - Ieteicamais reljefs: līdzens vai kalnains. Slīpums Berberu @@ -2415,7 +2414,7 @@ No Afganistānas līdz Zimbabvei, no Austrālijas līdz ASV, Argentīna, Brazīl Rādīt karti Maršruts aprēķināts Turp un atpakaļ - Jums vajag pievienot vismaz vienu marķieri, lai lietotu šo funkciju. + Lai lietotu šo funkciju, vajag pievienot vismaz vienu marķieri. Piezīmei nevarēja veikt izmaiņas Labot piezīmi !abot OSM piezīmi @@ -2429,7 +2428,7 @@ No Afganistānas līdz Zimbabvei, no Austrālijas līdz ASV, Argentīna, Brazīl No ledus ceļiem un brasla Izvairīties no ledus ceļiem un brasliem. Lietot pozīciju - Pievienot jūsu atrašanās vietu kā sākuma punktu maršrutam. + Pievienot patreizējo atrašanās vietu kā sākuma punktu ideālā maršruta izveidei. Mana pozīcija Finišs Plānot maršrutu @@ -3333,4 +3332,19 @@ No Afganistānas līdz Zimbabvei, no Austrālijas līdz ASV, Argentīna, Brazīl Izvēlieties lietas, ko importēt. Lietot dev.openstreetmap.org nevis openstreetmap.org OSM piezīmju/ POI / GPX augšuielādei un testēšanai. Lietot dev.openstreetmap.org + Plānot maršrutu + Pēdējo reizi labots + Importēt treku + Atvērt saglabāto treku + Veidot jaunu maršrutu + Izvēlieties treka failu atvēršanai. + Vai tiešām gribat atmest visas izmaiņas ieplānotajā maršrutā\? + Griezt pirms + Griezt pēc + Mainīt maršruta veidu pirms + Mainīt maršruta veidu pēc + Pievienot segmentus + Sadalīt pirms + Sadalīt pēc + Pievienot jaunu segmentu \ No newline at end of file diff --git a/OsmAnd/res/values-pl/strings.xml b/OsmAnd/res/values-pl/strings.xml index 90220cf9e8..6a236b130d 100644 --- a/OsmAnd/res/values-pl/strings.xml +++ b/OsmAnd/res/values-pl/strings.xml @@ -4056,13 +4056,13 @@ Zmień trasę nawigacji Trasa nawigacji Wygasł - • OsmAnd Live aktualizacje przeniesiono do \"Pobrane > Aktualizacje\" + • Aktualizacje OsmAnd Live przeniesiono do \"Pobrane > Aktualizacje\" \n \n •Ślady mogą być pokolorowane względem wysokości, prędkości, wzniesień. \n \n • Dodano opcję zmiany wyglądu trasy nawigacji \n -\n • zaktualizowano okno \"Nagrywanie Trasy\" +\n • Zaktualizowano okno \"Nagrywanie Trasy\" \n \n %1$s → … diff --git a/OsmAnd/res/values-pt-rBR/phrases.xml b/OsmAnd/res/values-pt-rBR/phrases.xml index bfd6941d61..12ab9fa85f 100644 --- a/OsmAnd/res/values-pt-rBR/phrases.xml +++ b/OsmAnd/res/values-pt-rBR/phrases.xml @@ -3918,4 +3918,13 @@ Jiu-jitsu Karatê Aros + Clube social + Planalto + Escritório diplomático + Kickboxe + Polo de bicicleta + Curling + Mergulho de falésia + Zurkhaneh + Tipo de baía \ 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 620a3dd28e..18e838ce27 100644 --- a/OsmAnd/res/values-pt-rBR/strings.xml +++ b/OsmAnd/res/values-pt-rBR/strings.xml @@ -3758,7 +3758,7 @@ Nome do arquivo %s arquivos de trilha selecionados O registro de trilhas fará uma pausa quando o aplicativo for encerrado (por meio de aplicativos recentes). (A indicação de segundo plano do OsmAnd desaparece da barra de notificação do Android.) - Especifique o intervalo de registro para a gravação geral da trilha (ligado por meio do widget de \'gravação de viagem\' no mapa). + Especifique o intervalo de registro para o registro geral da trilha (ativado por meio do widget de \'gravação de viagem\' no mapa). Pausar gravação de viagem Retomar a gravação da viagem Padrão do sistema @@ -4049,15 +4049,15 @@ Linha de rota A linha de rota seria usada %1$s especificado no estilo de mapa selecionado: %2$s. Especifique a cor para o modo de mapa: %1$s. - "• As atualizações do OsmAnd Live foram movidas para \"Downloads > Atualizações\" + • As atualizações do OsmAnd Live foram movidas para \"Downloads > Atualizações\" \n -\n • As trilhas agora podem ser coloridas por altitude, velocidade ou inclinação. +\n• As trilhas agora podem ser coloridas por altitude, velocidade ou inclinação. \n -\n • Adicionada opção para alterar a aparência da linha da rota de navegação +\n• Adicionada opção para alterar a aparência da linha da rota de navegação \n -\n • Caixa de diálogo \"Gravação de viagem\" atualizada +\n• Caixa de diálogo \"Gravação de viagem\" atualizada \n -\n" +\n Você não tem nenhuma compra Novo dispositivo / nova conta Se você tiver alguma dúvida, entre em contato conosco em %1$s. diff --git a/OsmAnd/res/values-pt/phrases.xml b/OsmAnd/res/values-pt/phrases.xml index 0d47393185..5ae2a5c536 100644 --- a/OsmAnd/res/values-pt/phrases.xml +++ b/OsmAnd/res/values-pt/phrases.xml @@ -49,7 +49,7 @@ Queijaria Loja de chocolates Loja de cafés - Loja de conveniência + Loja de conveniência/minimercado Centro comercial Loja de bebidas Talho @@ -58,7 +58,7 @@ Loja de frutas e verduras Peixaria Confeitaria - Geladaria + Gelataria Supermercado Loja de chás Loja de massas @@ -80,7 +80,7 @@ Loja de tapetes Loja de caridade Loja de produtos de higiene pessoal - Pronto a vestir + Pronto-a-vestir Vestuário infantil Sapataria Loja de velas @@ -247,8 +247,8 @@ Tipo Tipo Tipo - Posição - Fonte de água + Localização + Origem da água Forma de pagamento Som Tipo @@ -284,9 +284,9 @@ Pago Fumar Entrega ao domicílio - Serviço ao volante - Serviço ao volante - Serviço de take-away + Serviço no carro parado + Serviço a conduzir + Comida para levar Cocktails Microcervejaria Serviço @@ -296,7 +296,7 @@ Lareira Superfície Nudismo - Dieta + Pratos Tipo de massagem Tendas Máquina de lavar roupa @@ -411,10 +411,10 @@ Reservatório elevado Comporta de eclusa Ponto de viragem fluvial - Represa;Açude + Represa/açude;Represa;Açude Barragem Moinho de água - Quebra-mar;Molhe + Quebra-mar/molhe;Quebra-mar;Molhe Espigão marítimo Subestação Transformador @@ -565,18 +565,18 @@ Sim Sede de concelho Sede de freguesia - Aldeia;Lugar + Aldeia/lugar;Aldeia;Lugar Moradia isolada Subúrbio Zona de cidade Bairro Localidade Horta comunitária - Quinta;Fazenda + Quinta/fazenda;Quinta;Fazenda Farmácia Hospital Consultório médico - Clínica;Centro de saúde;Unidade de Saúde;Posto Médico + Clínica/centro de saúde/USF/posto médico Primeiros socorros Dentista Clínica geriátrica @@ -694,7 +694,7 @@ Marco de fronteira Canhão histórico Castelo - Portão da cidade + Portão/arco de cidade Forte Chafariz Ruínas históricas @@ -721,7 +721,7 @@ Tobogã aquático Alojamento Hotel - Pensão;Albergaria;Hospedaria;Estalagem;Residencial + Pensão/albergaria/residenial;Estalagem;Residencial;Albergaria;Hospedaria;Casa de hóspedes Hostel Hotel estrada Abrigo de montanha @@ -732,24 +732,24 @@ Galpão Cabana de caça Local de culto - Cristianismo - Judaísmo - Islamismo - Sikhismo - Budismo - Hinduísmo - Xintoísmo - Taoísmo + Cristã + Judaica + Islâmica + Siquista + Budista + Hinduísta + Xintoísma + Taoísma Vodu - Unitário-Universalismo + Unitário-universalista Multirreligiosa - Jainismo - Espiritualismo - Bahaísmo - Cientologismo - Paganismo + Jainista + Espiritualista + Bahaísta + Cientologista + Paganista Tenrikyo - Zoroastrismo + Zoroastrista Católica Batista Católica romana @@ -828,7 +828,7 @@ Agência de viagens Miradouro Local de acampamento - Local de caravanas + Parque de caravanas Mesa de piquenique Nascente Fonte termal @@ -890,7 +890,7 @@ Circo Galeria de arte Pista de dança - Discoteca;Danceteria + Discoteca/danceteria;Discoteca;Danceteria Clube de striptease Resort de esqui Resort de praia @@ -912,9 +912,9 @@ Restaurante Comida rápida Bar - Taberna + Taberna/pub/tasca;Taberna;Tasco;Tasca;Pub;Boteco;Buteco;Botequim Praça de alimentação - Fonte de água potável + Bebedouro (água potável) Churrasqueira Máquinas agrícolas Cesteiro @@ -967,7 +967,7 @@ Montagem de computadores Salão de beleza Manicura - Cabeleireiro + Cabeleireiro/a Salão de massagens Estúdio de tatuagem Limpeza de roupa a seco @@ -977,8 +977,8 @@ Ponto de boleia solidária de carro Ponto de barcos partilhados Doca - Linha de corte florestal;Atalhada;Linha corta-fogo - Casa de banho;Banheiros + Linha de corte florestal/atalhada;Atalhada;Linha corta-fogo;Linha de corte florestal + Casa de banho;Banheiros;WC Chuveiros públicos Sauna Bordel @@ -1002,9 +1002,9 @@ Cumeeira Glaciar Sumidouro - Queda de água;Cascata;Salto;Catarata + Queda de água/cascata/catarata/salto;Cascata;Salto;Catarata;Queda de água Rio - Ribeiro;Ribeira;Córrego + Ribeiro(a) Rápidos Cabo Praia @@ -1122,7 +1122,7 @@ Wiki em chinês Wiki em africâner Wiki em alsaciano - Wiki em azeri + Wiki em azerbaijanês Wiki em bengali Wiki em bishnupriya Wiki em bretão @@ -1246,8 +1246,8 @@ Não Sim Não - Vigiado - Não vigiado + Vigiado: sim + Vigiado: não Sim Não Estação seca @@ -1257,7 +1257,7 @@ Outono Inverno Com semáforos - Não controlado (sem semáforo e barreira) + Não controlado (sem semáforo nem barreira) Não sinalizado (sem marcas nem semáforo) Data de início/inauguração Acesso de cadeiras de rodas @@ -1384,12 +1384,12 @@ Mineral Lama Sulfurosa - Ponto de água + Ponto de água em grande quantidade (para caravanas ou garrafões) Poste com direções Painel Mapa Posto de turismo - Sinalização de rota + Baliza de caminho Terminal eletrónico de informação Sinalização de rota Mapa de caminhadas @@ -1433,7 +1433,7 @@ Centro equestre Área de lazer comum Jardim - Charneca;Mato de vegetação rasteira + Charneca/mato de vegetação rasteira;Charneca;Mato de vegetação rasteira Relvado Pradaria Matagal @@ -1555,7 +1555,7 @@ Não aceita cheque PIKEPASS Não aceita PIKEPASS - PIKEPASS (designado) + PIKEPASS (assinalado) Cartão Visa Débito Não aceita cartão Visa Débito Não aceita criptomoedas @@ -1650,36 +1650,36 @@ Não aceita Yandex.Money Detalhes de pagamento Salão de eventos - Vegetariana - Vegetariana (alguns) - Apenas vegetariana - Vegetariana - Dieta vegetariana: não - Vegana - Apenas vegana - Vegana - Dieta vegana: não - Livre de glúten - Apenas livre de glúten - Livre de glúten - Dieta livre de glúten: não - Kosher - Apenas kosher - Kosher - Dieta kosher: não - Halal - Apenas halal - Halal - Dieta halal: não - Livre de lactose - Apenas livre de lactose - Livre de lactose - Dieta livre de lactose: não - Piscitariana + Pratos vegetarianos: sim + Pratos vegetarianos: alguns + Pratos vegetarianos: só vegetarianos + Pratos vegetarianos: sim + Pratos vegetarianos: não + Pratos veganos: sim + Pratos veganos: só veganos + Pratos veganos: sim + Pratos veganos: não + Pratos livres de glúten: sim + Pratos livres de glúten: unicamente + Pratos livres de glúten: sim + Pratos livres de glúten: não + Pratos kosher (judaica): sim + Pratos kosher (judaica): unicamente + Pratos kosher (judaica): sim + Pratos kosher (judaica): não + Pratos halal (árabe): sim + Pratos halal (árabe): apenas + Pratos halal (árabe): sim + Pratos halal (árabe): não + Pratos sem lactose: sim + Pratos sem lactose: unicamente + Pratos sem lactose: sim + Pratos sem lactose: não + Pratos piscitariana (peixes e vegetais): sim Sim - Serviço ao volante: não + Serviço a conduzir: não Sim - Serviço ao volante: não + Serviço a conduzir: não Nome da cervejaria Sim Sem microcervejaria @@ -1704,7 +1704,7 @@ Produtos usados: não Produtos usados: unicamente Peças - Representante + Representante de marca Reparação Sem reparação Reparação de veículos elétricos @@ -1771,7 +1771,7 @@ Brinquedos Gelados Cartões de telemóvel (SIM) - Filial;Sucursal + Filial/sucursal;Filial;Sucursal Memorial de guerra Placa comemorativa Estátua @@ -2022,7 +2022,7 @@ Formação: ioga Formação: arte marcial Formação: aviação - Formação: cabeleireiro + Formação: cabeleireiro/a Estrutura monumental Tipo: indústria petrolífera Tipo: poço @@ -2242,7 +2242,7 @@ Condição dos degraus: regular Condição dos degraus: irregular Condição dos degraus: acidentada - Moledro;Moledo;Melédro;Mariola + Moledro/mariola;Moledo;Melédro;Mariola;Moledro Desfibrilhador Desfibrilhador: sim Tipo: túmulo de guerra @@ -2360,12 +2360,12 @@ Tipo de fortificação: arandela Tipo de fortificação: vala circular Pa (assentamento fortificado maori) - Quinta histórica;Fazenda histórica + Quinta/fazenda histórica;Quinta histórica;Fazenda histórica Estação ferroviária histórica Eira histórica Forca histórica Ferrovia histórica - Praça;Praceta + Praça/praceta/largo Artista Escultor Tipo de edifício: igreja @@ -2439,7 +2439,7 @@ Cirurgia maxilofacial Radiologia diagnóstica Gastroenterologia - Odontologia + Medicina dentária (odontologia) Nefrologia (doenças renais) Neurocirurgia Neuropsiquiatria @@ -2460,7 +2460,7 @@ Toxicologia clínica Optometria Patologia clínica - Fonoaudiologia + Fonoaudiologia (terapia da fala) Terapia manual Medicina do desporto Podologia @@ -2476,7 +2476,7 @@ Comportamental Medicina paliativa Tipo de edifício: pirâmide - Ginásio;Academia desportiva + Ginásio/academia desportiva;Ginásio;Academia desportiva Exercício físico Bilhar Forno microondas: sim @@ -2764,7 +2764,7 @@ Profundidade Sal Sal: não - Designado + Assinalado Rejeito de mineração Acampamento de verão Centro de recreativo @@ -2908,28 +2908,28 @@ Hambúrguer Café Sanduíches - Kebab - Döner kebab (shawarma) + Kebab (Médio Oriente) + Döner kebab (turco) Frango Gelados Sushi Peixe e batatas fritas - Frutos do mar + Marisco Churrascos - Noodles + Noodles (asiático) Donuts - Ramen + Ramen (japonês) Crepes Café da manhã - Gyudon + Gyudon (japonês) Bifes Tapas - Gastropub - Heuriger + Gastropub (inglês) + Heuriger (austríaco e alemão) Caril - Buschenschank + Buschenschank (austríaco e alemão) Grelhados - Bagel + Bagel (polaco) Café-restaurante Linguiças Bolos @@ -2949,26 +2949,26 @@ Iogurtes gelados Comida frita Panquecas salgadas - Bistrô + Bistrô (francês) Padaria Cuscuz Frango frito Sobremesas Cantina - Tacos - Piadina - Falafel + Tacos (mexicano) + Piadina (italiano) + Falafel (Médio Oriente) Batidos de frutas - Souvlaki + Souvlaki (grego) Salgados Iogurtes - Gyros + Gyros (grego) Empanadas Crepes - Yakiniku + Yakiniku (japonês) Suki tailandês Udon (japonês) - Brasserie + Brasserie (francês) Chá de bolhas Yakitori (japonês) Sagardotegia (basco) @@ -2978,11 +2978,11 @@ Chocolates Vinhos Batatas - Brunch + Brunch (inglês e norte-americano) Sandes - Pita - Fondue - Baguetes + Pita (judaico) + Fondue (suíço) + Baguetes (francês) Pastéis Burritos Teriyaki (japonês) @@ -3040,7 +3040,7 @@ Latino-americana Nepalesa Mongol - Oriente Médio + Médio Oriente Ucraniana Afegã Belga @@ -3082,7 +3082,7 @@ Suplementos alimentares Estúdio de fotografia Penhasco - Cativeiro de animais;Refúgio de animais + Refúgio de animais;Cativeiro de animais Cativeiro de animais: cavalos Cativeiro de animais: ovelhas Tipo: cercado @@ -3118,7 +3118,7 @@ Estandes Vendas Vendas: não - Vendas: sim; usados + Vendas: sim, usados Vendas: usados Aluguer Aluguer: não @@ -3181,7 +3181,7 @@ Lanchas: não Casas-barco: sim Casas-barco: não - Gaivota: sim + Gaivotas a pedais: sim Gaivota: não Motas de água: sim Motas de água: não @@ -3203,7 +3203,7 @@ Instituição governamental de transportes Instituição legislativa governamental Canal VHF - Desfiladeiro;Canhão + Desfiladeiro/canhão;Desfiladeiro;Canhão Ravina Área montanhosa Argila @@ -3249,69 +3249,69 @@ Comunidade Família Várias famílias - Tomada: CEE azul - Tomada: CEE azul: corrente - Tomada: CEE azul: saída - Tomada: CEE vermelha 16A - Tomada: CEE vermelha 16A: corrente - Tomada: CEE vermelha 16A: saída - Tomada: CEE vermelha 32A - Tomada: CEE vermelha 32A: corrente - Tomada: CEE vermelha 32A: saída - Tomada: CEE vermelha 64A - Tomada: CEE vermelha 64A: corrente - Tomada: CEE vermelha 64A: saída - Tomada: CEE vermelha 125A - Tomada: CEE vermelha 125A: corrente - Tomada: CEE vermelha 125A: saída - Tomada: Tipo 1 - Tomada: Tipo 1: corrente - Tomada: Tipo 1: saída - Tomada: Tipo 1 combo - Tomada: Tipo 1 combo: corrente - Tomada: Tipo 1 combo: saída - Tomada: Tipo 2 - Tomada: Tipo 2: corrente - Tomada: Tipo 2: saída - Tomada: Tipo 2 combo - Tomada: Tipo 2 combo: corrente - Tomada: Tipo 2 combo: saída - Tomada: Tipo 3 - Tomada: Tipo 3: corrente - Tomada: Tipo 3: saída - Tomada: CHAdeMO - Tomada: CHAdeMO: corrente - Tomada: CHAdeMO: saída - Tomada: Padrão Tesla - Tomada: Padrão Tesla: corrente - Tomada: Padrão Tesla: saída - Tomada: Tesla Supercharger - Tomada: Tesla Supercharger: corrente - Tomada: Tesla Supercharger: saída - Tomada: Tesla Roadster - Tomada: Tesla Roadster: corrente - Tomada: Tesla Roadster: saída - Tomada: NEMA 5-15R - Tomada: NEMA 5-15R: corrente - Tomada: NEMA 5-15R: saída - Tomada: NEMA 5-20 - Tomada: NEMA 5-20: corrente - Tomada: NEMA 5-20: saída - Tomada: NEMA 14-30 - Tomada: NEMA 14-30: corrente - Tomada: NEMA 14-30: saída - Tomada: NEMA 14-50 - Tomada: NEMA 14-50: corrente - Tomada: NEMA 14-50: saída - Tomada: Schuko - Tomada: Schuko: corrente - Tomada: Schuko: saída - Tomada: BS 1363 - Tomada: BS 1363: corrente - Tomada: BS 1363: saída - Tomada: AS/NZS 3112 - Tomada: AS/NZS 3112: corrente - Tomada: AS/NZS 3112: saída + Tomadas: CEE azul + Tomadas: CEE azul: amperes + Tomadas: CEE azul: watts + Tomadas: CEE vermelha 16A + Tomadas: CEE vermelha 16A: amperes + Tomadas: CEE vermelha 16A: watts + Tomadas: CEE vermelha 32A + Tomadas: CEE vermelha 32A: amperes + Tomadas: CEE vermelha 32A: watts + Tomadas: CEE vermelha 64A + Tomadas: CEE vermelha 64A: amperes + Tomadas: CEE vermelha 64A: watts + Tomadas: CEE vermelha 125A + Tomadas: CEE vermelha 125A: amperes + Tomadas: CEE vermelha 125A: watts + Tomadas: Tipo 1 + Tomadas: Tipo 1: amperes + Tomadas: Tipo 1: watts + Tomadas: Tipo 1 combo + Tomadas: Tipo 1 combo: amperes + Tomadas: Tipo 1 combo: watts + Tomadas: Tipo 2 + Tomadas: Tipo 2: amperes + Tomadas: Tipo 2: watts + Tomadas: Tipo 2 combo + Tomadas: Tipo 2 combo: amperes + Tomadas: Tipo 2 combo: watts + Tomadas: Tipo 3 + Tomadas: Tipo 3: amperes + Tomadas: Tipo 3: watts + Tomadas: CHAdeMO + Tomadas: CHAdeMO: amperes + Tomadas: CHAdeMO: watts + Tomadas: Padrão Tesla + Tomadas: Padrão Tesla: amperes + Tomadas: Padrão Tesla: watts + Tomadas: Tesla Supercharger + Tomadas: Tesla Supercharger: amperes + Tomadas: Tesla Supercharger: watts + Tomadas: Tesla Roadster + Tomadas: Tesla Roadster: amperes + Tomadas: Tesla Roadster: watts + Tomadas: NEMA 5-15R + Tomadas: NEMA 5-15R: amperes + Tomadas: NEMA 5-15R: watts + Tomadas: NEMA 5-20 + Tomadas: NEMA 5-20: amperes + Tomadas: NEMA 5-20: watts + Tomadas: NEMA 14-30 + Tomads: NEMA 14-30: amperes + Tomadas: NEMA 14-30: watts + Tomadas: NEMA 14-50 + Tomadas: NEMA 14-50: amperes + Tomadas: NEMA 14-50: watts + Tomadas: Schuko + Tomadas: Schuko: amperes + Tomadas: Schuko: watts + Tomadas: BS 1363 + Tomadas: BS 1363: amperes + Tomadas: BS 1363: watts + Tomadas: AS/NZS 3112 + Tomadas: AS/NZS 3112: amperes + Tomadas: AS/NZS 3112: watts Carro: sim Carro: não Bicicleta: sim @@ -3539,7 +3539,7 @@ Comboio Autocarro Metropolitano - Via para veículos com alta ocupação + Veículos com alta ocupação Elétrico Balsa/ferry sem categoria @@ -3572,7 +3572,7 @@ Mesa muda-fraldas: sim Mesa muda-fraldas: não Mesa muda-fraldas: limitada - Mesa muda-fraldas; sala + Mesa muda-fraldas: sala Local da mesa muda-fraldas: WC masculino Local da mesa muda-fraldas: WC feminino Local da mesa muda-fraldas: WC unissexo @@ -3650,11 +3650,11 @@ Acesso a autocaravanas: não Acesso a reboques: não Acesso a transportes públicos: sim - Acesso a transportes públicos: designado + Acesso a transportes públicos: assinalado Acesso a transportes públicos: não Acesso a autocarros: não Acesso a autocarros turísticos: sim - Acesso a autocarros turísticos: designado + Acesso a autocarros turísticos: assinalado Acesso a autocarros turísticos: não Acesso a autocarros de longa distância: sim Acesso a autocarros de longa distância: não @@ -3665,37 +3665,37 @@ Acesso a veículos agrícolas: sim Acesso a veículos agrícolas: não Acesso a táxis: sim - Acesso a táxis: designado + Acesso a táxis: assinalado Acesso a táxis: não Acesso a deficientes: não Sim - Designado + Assinalado Tolerado Sim - Designado + Assinalado Sim - Designado + Assinalado Sim - Designado + Assinalado Sim - Designado + Assinalado Sim Tolerado Destino - Designado + Assinalado Sim - Designado + Assinalado Sim - Designado + Assinalado Sim Destino Tolerado - Designado + Assinalado Entregas Sim - Designado + Assinalado Sim - Designado + Assinalado Entrada da adega Alimentação saudável Construtor @@ -3779,7 +3779,7 @@ Incorreto Primitivo Contrastado - Apenas quando é permitido caminhar + Apenas quando é permitido atravessar Acesso à Internet: só clientes Não Sim @@ -3810,11 +3810,11 @@ Vibração Quarteirão Município - Caixa livre;Caixa de donativos;Give-box + Caixa livre/de donativos;Give-box;Caixa livre;Caixa de donativos Seta: não Elevador - Calendário - Tempo real + Horário + Atualizado em tempo real Atraso Sim Quadro de partidas: não @@ -3917,13 +3917,15 @@ Posição de paragem da biblioteca itinerante Estado da pista: fechada Estado da pista: aberta - Vigiado: não - Vigiado: sim + Supervisionado: não + Supervisionado: sim Nome da pista Salto com esqui Passagem de vida selvagem - Lavadoiro público + Lavadouro público Poço Gabinete diplomático Tipo de baía + Planalto + Clube social \ No newline at end of file diff --git a/OsmAnd/res/values-pt/strings.xml b/OsmAnd/res/values-pt/strings.xml index eaa5dd4833..9665801ba0 100644 --- a/OsmAnd/res/values-pt/strings.xml +++ b/OsmAnd/res/values-pt/strings.xml @@ -20,11 +20,11 @@ Desporto Alimentação Turismo - Transporte - Indexando endereço… - Indexando mapa… - Indexando POI… - Indexando transporte… + Transportes públicos + A indexar endereços… + A indexar o mapa… + A indexar POI… + A indexar transportes… Erro de entrada/saída km km/h @@ -37,7 +37,7 @@ Supermercado Para turistas Combustível - Pesquisar nome on-line + Pesquisar nome online A ler mosaicos temporários… O índice \'\'{0}\'\' não coube na memória A versão do índice \'\'{0}\'\' não é suportada @@ -96,7 +96,7 @@ Não foi possível ler os dados de GPX. Mapas vetoriais offline Procurar transporte na paragem - Alterar POI + Alterar POI OSM Eliminar POI Direção da bússola Direção do movimento @@ -120,7 +120,7 @@ Favoritos Notas OSM (online) Camada superior de POI… - Origem dos mapas… + Repositório de mapas… Camada de mapa Busca por POI Utilizar um dispositivo trackball para mover o mapa. @@ -159,12 +159,12 @@ \nAgora só é possível ver o mapa pré-carregado, não descarregar novas áreas. A descomprimir o ficheiro… Vire à direita e continue em - Vire fortemente à direita e continue em - Vire levemente à direita e continue em + Vire acentuadamente à direita e continue em + Vire ligeiramente à direita e continue em Vire à esquerda e continue em - Vire fortemente à esquerda e continue em - Vire levemente à esquerda e continue em - Inverta o sentido da marcha e continue em + Vire acentuadamente à esquerda e continue em + Vire ligeiramente à esquerda e continue em + Faça inversão de marcha e continue em Comece em Continuar Descarregar regiões @@ -214,14 +214,14 @@ Não foi encontrado nada A pesquisar… A pesquisar o endereço … - Pesquisa on-line usando OSM Nominatim + Pesquisa na Internet usando OSM Nominatim Pesquisa online: número da casa, rua, cidade Pesquisa offline Pesquisa online Nível máximo de zoom - Não navegar em mapas on-line para níveis de ampliação além deste. + Não navegar em mapas online para níveis de ampliação além deste. Distância total %1$s, tempo de viagem %2$d h %3$d min. - Serviço de navegação on-line ou off-line. + Serviço de navegação online ou offline. Serviço de navegação A pasta de armazenamento não está acessível no cartão de memória! Descarregar {0} - {1} \? @@ -264,7 +264,7 @@ A carregar dados… A ler dados locais… O OsmAnd foi interrompido inesperadamente. O ficheiro de registo encontra-se em {0}. Por favor, relate o problema e inclua o ficheiro de registo. - A gravar o ficheiro GPX… + A guardar o ficheiro GPX… Concluído Utilizar a Internet para calcular uma rota. Utilizar navegação online @@ -341,7 +341,7 @@ Edifício Rua transversal Atualizar mapa - Criar POI OSM + Criar POI Sim Cancelar Aplicar @@ -371,7 +371,7 @@ Erro de entrada/saída na execução da ação {0}. As informações sobre o objeto não foram carregadas Aberto - Comentário + Comentar Alterar POI Todas as outras etiquetas são preservadas Enviar @@ -395,18 +395,18 @@ Definir a transparência (0 - transparente, 255 - opaco) Cancelar o descarregamento\? O mapa base, necessário para fornecer funcionalidade básica, está na fila de descarregamentos. - Mosaicos de mapas on-line e em ficheiros temporários + Mosaicos de mapas online e em ficheiros temporários Mapas padrão (vetorial) - Ative a extensão de \'Mapas on-line\' para selecionar diferentes origens de mapas - Mapas on-line e mosaicos + Ative a extensão de \'Mapas online\' para selecionar diferentes origens de mapas + Mapas online e mosaicos Usar mapas online (descarregar e armazenar mosaicos no cartão de memória). Mapas on-line - Configurar origens de mosaicos ou de mapas on-line. - Aceda a muitos tipos de mapas on-line (também chamados de mosaico ou raster) pré-definidos do OpenStreetMap (como Mapnik) para imagens de satélite e camadas especiais, como mapas aquáticos, climáticos, geológicos, camadas de sombra de relevo, etc. + Configurar origens de mosaicos ou de mapas online. + Aceda a muitos tipos de mapas online (também chamados de mosaico ou raster) pré-definidos do OpenStreetMap (como Mapnik) para imagens de satélite e camadas especiais, como mapas aquáticos, climáticos, geológicos, camadas de sombra de relevo, etc. \n -\nQuaisquer desses mapas podem ser usados como mapa principal (base) para ser mostrado no OsmAnd ou na camada superior ou inferior para outro mapa base (como o mapa off-line normal de OsmAnd). Para tornar qualquer camada inferior do mapa mais visível, certos elementos do mapa vetorial do OsmAnd podem facilmente ser ocultados através do menu \'Configurar mapa\'. +\nQuaisquer desses mapas podem ser usados como mapa principal (base) para ser mostrado no OsmAnd ou na camada superior ou inferior para outro mapa base (como o mapa offline normal de OsmAnd). Para tornar qualquer camada inferior do mapa mais visível, certos elementos do mapa vetorial do OsmAnd podem facilmente ser ocultados através do menu \'Configurar mapa\'. \n -\nOs mosaicos dos mapas podem ser obtidos diretamente através de origens on-line ou podem ser preparados para uso off-line (e copiados manualmente para o diretório de dados do OsmAnd) como uma base de dados sqlite, que pode ser produzido por uma variedade de ferramentas de terceiros para preparação de mapas. +\nOs mosaicos dos mapas podem ser obtidos diretamente através de origens online ou podem ser preparados para uso offline (e copiados manualmente para o diretório de dados do OsmAnd) como uma base de dados sqlite, que pode ser produzido por uma variedade de ferramentas de terceiros para preparação de mapas. Mostra as configurações para ativar o rastreamento em segundo plano e a navegação, despertando periodicamente o dispositivo GPS (com o ecrã desligado). Mostra os recursos de acessibilidade do dispositivo disponíveis diretamente no OsmAnd. Facilita, por exemplo, o ajuste da velocidade da fala para vozes TTS, configurando a navegação no ecrã do teclado direcional, usando um trackball para controlo da ampliação, ou feedback texto-para-fala, por exemplo, para anunciar automaticamente a sua posição. Mostra configurações para recursos de desenvolvimento e depuração como testar ou simular roteamento, o desempenho de renderização do ecrã ou solicitação de voz. Essas configurações são destinadas a programadores e não são necessárias para o utilizador em geral. @@ -465,7 +465,7 @@ para a esquerda para a frente à esquerda horas - em direção a + para Precisão Altitude Sem informação @@ -493,11 +493,11 @@ Edição assíncrona de OSM: POIs / notas guardados no dispositivo Mostrar e gerir POIs/notas do OSM guardados localmente. - Especifique o intervalo de rastreamento on-line. - Intervalo de rastreamento on-line + Especifique o intervalo de rastreamento online. + Intervalo de rastreamento online Especifique o endereço web com a sintaxe de parâmetros: lat={0}, lon={1}, data/hora={2}, hdop={3}, altitude={4}, velocidade={5}, bearing={6}. - Endereço web de rastreamento on-line - Registo de trajeto usando widget GPX ou via \'Gravação de viagem\' nas configurações. + Endereço web de rastreamento online + Registo de trajeto usando widget GPX ou via \'Gravar viagem\' nas configurações. Mostrar rota atual Pode descarregar ou atualizar %1$s mapas. Versão gratuita @@ -562,7 +562,7 @@ Sem definir Centro do mapa atual Origem: - Pesquisar nas proximidades + Pesquisar perto daqui Rota guardada como \'%1$s\'. Nome do ficheiro: Já existe um ficheiro com o mesmo nome. @@ -646,7 +646,7 @@ Pesquisa por localização geográfica Sistema Idioma de visualização da aplicação (após reinicialização do OsmAnd). - Linguagem + Idioma Seguinte Anterior Alterar a unidade de medida de distância. @@ -665,9 +665,9 @@ Partilhar localização Foi adicionado o ponto de passagem GPX \'\'{0}\'\' Acrescentar ponto ao trilho GPX gravado - Navegação OsmAnd off-line ainda é uma função experimental e não funciona em distâncias superiores a cerca de 20 km. + Navegação OsmAnd offline ainda é uma função experimental e não funciona em distâncias superiores a cerca de 20 km. \n -\nO serviço de navegação está temporariamente mudado para CloudMade on-line. +\nO serviço de navegação está temporariamente mudado para CloudMade online. Não foi possível encontrar a pasta especificada. Local de armazenamento Todos os dados offline na aplicação instalada antiga serão suportados pela nova, mas os pontos Favoritos devem ser exportados da aplicação antiga e depois importados na nova. @@ -678,9 +678,9 @@ A carregar compilações OsmAnd… Selecionar a compilação OsmAnd a instalar Instalar versão - DDD.DDDDD - DDD MM.MMM - DDD MM SS.S + GGG.GGGGG + GGG MM.MMM + GGG MM SS.S Tornar transparentes todas as características do terreno no mapa. Polígonos Modo de visualização @@ -689,9 +689,9 @@ Mostrar curvas de nível Aumentar o nível de detalhe do mapa. Ver mais detalhes no mapa - Rotunda: saia na saída %1$d para - Mantenha-se à esquerda e continue - Mantenha-se à direita e continue + Rotunda: saia na %1$dª saída para + Mantenha-se à esquerda e continue em + Mantenha-se à direita e continue em Assim que possível Público Identificável @@ -706,7 +706,7 @@ Selecione o tipo de estacionamento Tempo limitado Sem limite de tempo - Adicionar uma notificação à aplicação Calendário + Adicionar notificação ao Calendário Estacionamento com limite de tempo Estacionamento sem limite de tempo A localização do seu veículo estacionado. %1$s @@ -717,7 +717,7 @@ Permite gravar onde o seu carro foi estacionado e quanto tempo de estacionamento resta (se houver um limite de tempo). \nA localização e o tempo ficam visíveis no painel de controlo do OsmAnd e num widget no ecrã do mapa. Um alarme pode ser adicionado ao calendário Android como lembrete. Local de estacionamento - Marcar como local de estacionamento + Marcar estacionamento Eliminar um marcador de estacionamento Ponto de partida demasiado distante da estrada mais próxima. Local partilhado @@ -736,7 +736,7 @@ Continuar a navegação a seguir que ficou antes inacabada\? (%1$s segundos) Radares de velocidade Informações de circulação - Sem estradas com portagem + Evitar estradas com portagem Nome da rua Configuração do ecrã Onde estou @@ -763,10 +763,10 @@ \na execução em segundo plano Mostrar alertas… Configure avisos de trânsito (limites de velocidade, portagens, lombas, passadeiras, túneis), radares e a faixa de rodagem. - Sem autoestradas + Evitar autoestradas Ajustar à estrada Visualização e navegação móvel de mapas globais do OSM offline e online - OsmAnd é uma aplicação de navegação de código aberto para mapas off-line e on-line + OsmAnd é uma aplicação de navegação de código aberto para mapas offline e online Criar filtro de POI Meio de transporte: Nascer do sol: %1$s @@ -776,8 +776,8 @@ Estilo de renderização Configurar ecrã Mostrar faixas de rodagem - Sem estradas não pavimentadas - Sem balsas/ferries + Evitar estradas não pavimentadas + Evitar balsas/ferries Evitar… Rotas fluorescentes Régua @@ -810,7 +810,7 @@ Selecione um esquema de cores de estrada: Esquema de cores Ver direção para o destino - Ative a extensão de \"Gravação de viagem\" para usar serviços de registo de posição (registo GPX, rastreamento on-line) + Ative a extensão de \"Gravar viagem\" para usar serviços de registo de posição (registo GPX, rastreamento online) Calcular rota possivelmente não ideal em longas distâncias Ative o GPS nas configurações Serviços de registo @@ -852,7 +852,7 @@ Pesquisar mais povoações/códigos postais Sincroniza trilhos e notas de vídeo/áudio com a sua conta Dropbox. Gravar vídeo - Gravar audio + Gravar áudio Ação predefinida do widget: Ação widget padrão Formato de saída de vídeo @@ -866,7 +866,7 @@ Eliminar este item\? Gravar nota de áudio Gravar nota de vídeo - Camada de gravação + Camada trilhos gravados Não foi possível reproduzir a gravação. Eliminar a gravação Reproduzir @@ -883,7 +883,7 @@ Tem a certeza que quer limpar o seu destino (e destinos intermédios)\? Calcula rotas precisas sem falhas, mas com distância limitada e lento. Roteamento preciso (alfa) - Tirar uma foto + Tirar uma fotografia Extensão Dropbox Alterar ordem Por favor, considere comprar a extensão \'Curvas de nível\' para apoiar o desenvolvimento. @@ -891,7 +891,7 @@ A pedido\? Formato de saída de vídeo: Usar gravador do sistema para vídeo. - Utilizar a aplicação do sistema para fotos. + Utilizar a aplicação do sistema para tirar fotografias. Usar aplicação da câmara A reproduzir o áudio da gravação. \n%1$s @@ -901,7 +901,7 @@ Parar Iniciar Notas de áudio/vídeo - Extensão OsmAnd para curvas de nível off-line + Extensão OsmAnd para curvas de nível offline Medição da distância O local para associar à nota ainda não está definido. Toque em \"Usar posição…\" para atribuir uma nota ao local especificado. Notas de áudio @@ -909,8 +909,8 @@ Notas de áudio/vídeo Partes Curvas de nível - Foto %1$s de %2$s - Tirar uma foto + Fotografia %1$s de %2$s + Tirar uma fotografia Esta extensão disponibiliza \'Curvas de nível\' e \'Sombras de relevo\', que podem ser aplicadas nos mapas padrão do OsmAnd. Estas funcionalidades podem ser apreciadas por atletas, caminhantes e qualquer pessoa interessada na informação do relevo de uma paisagem. \n \nOs dados globais (entre as latitudes 70° norte e 70° sul) são baseados nas medições do SRTM (Shuttle Radar Topography Mission) e do ASTER (Advanced Spaceborn Thermal Emission and Reflection Radiometer), um instrumento de imagens no satélite \'Terra\', o satélite principal do Sistema de Observação da Terra da NASA. O ASTER é um esforço conjunto da NASA, do Ministério da Economia, Comércio e Indústria do Japão e do Sistema Espacial Japonês (J-spacesystems). @@ -920,7 +920,7 @@ Curvas de nível Outros mapas Curvas de nível - Limites + Fronteiras regionais Ocultar a visualização de limites regionais (níveis de administração 5 – 9). Ver desmarcado @@ -948,7 +948,7 @@ %1$d ficheiros restantes Faltam %1$d ficheiros para descarregar Versão completa - Descartar a rota\? + Cancelar a rota\? Parar navegação Ficheiro de alterações OSM %1$s gerado Não foi possível fazer a cópia de segurança das alterações do OSM. @@ -985,7 +985,7 @@ Lat. %1$.3f, Lon. %2$.3f Para: Via: - Desde: + De: Destino %1$s Definir como destino Selecione primeiro a cidade ou rua @@ -1018,10 +1018,10 @@ Focagem automática Foco hiperfocal Profundidade de campo alargada (EDOF) - O foco está definido como infinito + Focar infinito Focagem macro (close-up) A câmara tenta focar continuadamente - Reproduzir o som do obturador da câmara + Reproduzir som ao tirar fotografias Definir som ou silêncio ao fotografar. Canadá Versão: @@ -1082,7 +1082,7 @@ Aeronave Tem a certeza que quer eliminar %1$d alterações no OSM\? Eliminar tudo - Wikipédia (off-line) + Wikipédia (offline) Marca marítima Escolha os perfis a mostrar. Perfis da aplicação @@ -1113,7 +1113,7 @@ Inglês Africâner Arménio - Cálculo off-line do segmento de rota OsmAnd + Calcular segmento de rota OsmAnd sem Internet Calcular rota de OsmAnd para o primeiro e último segmento da rota Usar o trajeto indicado para a navegação\? Adicionar como destino posterior @@ -1125,13 +1125,13 @@ Informações da rota Preferir autoestradas Preferir autoestradas - Sem estradas com portagem + Evitar portagens Evitar estradas com portagem - Sem estradas não pavimentadas + Evitar estradas não pavimentadas Evitar estradas não pavimentadas - Sem balsas/ferries + Evitar balsas/ferries Evita balsas/ferries - Sem autoestradas + Evitar autoestradas Evita autoestradas Peso máximo Especifique o limite de peso permitido para veículos em rotas. @@ -1141,7 +1141,7 @@ A copiar o ficheiro (%s) para novo destino… A copiar os ficheiros OsmAnd para o novo destino (%s)… A copiar ficheiros de dados do OsmAnd… - Cálculo de rota OsmAnd off-line + Cálculo de rota OsmAnd offline Camião Basco Bielorrusso @@ -1226,7 +1226,7 @@ \n \nToque longo para ver no mapa" Iniciar a orientação passo a passo automaticamente - selecionado + selecionado(s) Intervalo de divisão Ponto de rota: %1$s Distância: %1$s (%2$s pontos ) @@ -1287,9 +1287,9 @@ Descarregar novo mapa Idioma do mapa Zona - Todos os descarregamentos + Disponíveis Atualizações - Local + Descarregados Não é possível descarregar. Verifique se tem ligação à Internet. Todos os ficheiros foram atualizados Usar o renderizador OpenGL @@ -1323,8 +1323,8 @@ Armazenamento do mapa Copiar Filtrar por nome - Digite para pesquisar tudo - Abrir + Digite para pesquisar em tudo + Aberto agora Assistente de mapeador OSM Informação A-GPS Gerir @@ -1348,12 +1348,12 @@ Descarregar compilações noturnas. Especifique um servidor proxy. Gerir - Transporte + Transportes públicos Rotas de elétricos e comboios Iluminação pública Predefinido Estradas em alto contraste - OsmAnd fornece mapas de navegação globais e navegação off-line. + OsmAnd fornece mapas de navegação globais e navegação offline. Bem-vindo(a) Rota atual Mudanças no OSM adicionadas ao conjunto de alterações local @@ -1385,7 +1385,7 @@ Desativar Ativado Desativado - Selecionado + Selecionado(s) Desmarcar Desmarcar tudo Exportar @@ -1398,7 +1398,7 @@ A gravar o trajeto Áudio Vídeo - Foto + Fotografia Pontos de rota Segmentos do trajeto Pontos do trajeto @@ -1410,13 +1410,13 @@ Partilhar nota Posição:\n Lat %1$s\n Lon %2$s Notas de áudio/vídeo - Mapa on-line + Mapa online Apenas estradas Pistas de esqui %1$s livre Memória do dispositivo - Para mostrar mapas de esqui, tem de descarregar o mapa off-line especial. - Para mostrar mapas náuticos, tem de descarregar o mapa off-line especial. + Para mostrar mapas de esqui, tem de descarregar o mapa offline especial. + Para mostrar mapas náuticos, tem de descarregar o mapa offline especial. Editar grupo Lugar de estacionamento Compilações @@ -1426,7 +1426,7 @@ Ligar o ecrã Configurar mapa Pontos de rota - Descartar + Cancelar Outros atributos do mapa Mostrar restrições de acesso e portagens Mostrar qualidade da via @@ -1435,7 +1435,7 @@ Colorir edifícios por tipo Escala de montanhismo (SAC) Camada superior de símbolos de montanhismo - Mostrar rotas para bicicletas + Rotas para bicicletas Edifícios Rotas de troleicarros Por favor, use um nome de categoria que ainda não exista. @@ -1471,7 +1471,7 @@ \'Desligado\' abre o mapa diretamente. Mostrar no arranque Copiado para a área de transferência - Guardar off-line + Guardar offline POI do OpenStreetMap alterado Escolha as estradas que quer evitar durante a navegação. Som @@ -1493,7 +1493,7 @@ Taxa de bits do áudio Escolha a taxa de bits do áudio. POI do OSM eliminado - Abrir nota OSM + Criar nota OSM Reabrir nota OSM Nota do OSM comentada Nota do OSM adicionada @@ -1513,7 +1513,7 @@ Edições OSM partilhadas via OsmAnd Ler mais Novidades - Objetos planeados + Elementos com construção planeada Enviar POI OSM adicionado Mapa base mundial (cobrindo o mundo inteiro em baixo nível de ampliação) ausente ou ultrapassado. Por favor, considere descarregá-lo para uma visão global. @@ -1523,7 +1523,7 @@ Mostrar mapa Define o sinalizador que indica a primeira inicialização da aplicação, mantém todas as outras configurações inalteradas. Simular arranque inicial da aplicação - geo: + Outras aplicações de mapas Partilhar localização Enviar Executar em segundo plano @@ -1535,7 +1535,7 @@ Ficheiro GPX com localizações. Localizações Extensões - Sem comboio vaivém + Evitar comboios vaivém Evita usar comboios vaivém, em pequenos trajetos predefinidos como aeroportos Perigo Contorno em negrito @@ -1545,8 +1545,8 @@ Padrão (ciano translúcido) Cor GPX Cor GPX - Espessura GPX - Espessura GPX + Espessura dos trilhos GPX + Espessura dos trilhos GPX Vermelho Vermelho translúcido Laranja translúcido @@ -1593,7 +1593,7 @@ Quer descarregar dados adicionais da Wikipédia (%1$s MB)\? O serviço de localização não está ativado. Quer ativá-lo\? Importar para o OsmAnd - Ler o artigo completo (on-line) + Ler o artigo completo (na Internet) Wikipédia Wikipédia Mostrar detalhes @@ -1626,7 +1626,7 @@ \n- Sincronizar grupos e dispositivos com o servidor; \n- Gerir grupos e dispositivos num painel de controlo pessoal no site web. Utilizador anónimo - Conectado como %1$s + Sessão iniciada como %1$s Não selecionado Mês e país: Tamanho das atualizações @@ -1659,7 +1659,7 @@ Modo do mapa Fino Médio - Negrito + Grosso Agora a aplicação está autorizada a gravar no armazenamento externo, mas primeiro é necessário reiniciar a aplicação. Mover ↑ Mover ↓ @@ -1675,7 +1675,7 @@ Número de destinatários Edições %1$s, posição %2$s, total de edições %3$s Classificação de editores do OSM - Assinatura do OsmAnd Live + Subscrição do OsmAnd Live Subscrever Necessário para o informar sobre as suas contribuições. Nome público @@ -1706,7 +1706,7 @@ Inserir nome do país Nova versão Primeiros passos com o OsmAnd - Escolha onde pretende guardar os ficheiros de mapas e de outros dados. + Escolha onde pretende guardar os ficheiros de mapas e outros dados. Outros Ativar a navegação para mudanças do OsmAnd Live. Navegação OsmAnd Live @@ -1725,10 +1725,10 @@ Obrigado por apoiar o OsmAnd! \nPara ativar todos os novos recursos tem que reiniciar o OsmAnd. Parte da sua doação será enviada aos utilizadores OSM que submetem alterações ao mapa nessa região. - Configurações da assinatura - Por favor primeiro compre a assinatura do OsmAnd Live - Esta assinatura permite atualizações hora a hora de todos os mapas à volta do mundo. -\nParte da renda regressa à comunidade do OpenStreetMap e paga-se para cada contribuição no OpenStreetMap. + Configurações da subscrição + Por favor primeiro compre a subscrição do OsmAnd Live + Esta subscrição permite atualizações hora a hora de todos os mapas à volta do mundo. +\nParte da renda regressa à comunidade do OpenStreetMap e é paga para cada contribuição no OpenStreetMap. \nSe gosta do OsmAnd e do OpenStreetMap e quer apoiar e ser apoiado por eles, esta é a maneira perfeita para o fazer. Selecione o marcador do mapa Outros marcadores @@ -1751,10 +1751,10 @@ Remover todos os marcadores ativos\? Limpar o histórico dos marcadores de mapa\? Marcadores ativos - Marcadores de mapa + Marcadores Marcador de mapa É recomendável desativar a renderização de polígono. - Mostrar trilhos de bicicletas de montanha + Trilhos de bicicletas BTT Mostrar polígonos Encontrar estacionamento Situação @@ -1764,11 +1764,11 @@ Remover Ler mais Remover atualizações descarregadas e voltar à edição do mapa original - Adicionar intervalo de tempo + Adicionar intervalo de tempo adicional Estrada bloqueada Selecionar Inverter ponto de partida e destino - Ícones POI + Ícones dos POI Item removido Itens removidos Desfazer tudo @@ -1776,7 +1776,7 @@ Ponto de partida Divisão das gravações Usar divisão das gravações - Duração do recorte + Duração da divisão Limite de tempo máximo para clipes gravados. Macedónio Servo-Croata @@ -1802,19 +1802,19 @@ Siga-nos Ignorar o descarregamento de mapas - Obter direções e descobrir novos lugares sem ligação à Internet + Obtenha direções e descubra novos lugares sem ser necessário estar ligado à Internet Ignorar - Selecione outra região + Selecionar outra região Alterar Cartão de memória - Não tem nenhum mapa off-line instalado. Pode escolher um mapa na lista ou descarregar mapas mais tarde através do \'menu - %1$s\'. + Não tem nenhum mapa offline instalado. Pode escolher um mapa na lista ou descarregar mapas mais tarde através do \'menu - %1$s\'. Marcadores Formato de coordenadas Usar teclado do sistema Escolher formato de introdução de coordenada. Poderá sempre alterá-lo ao selecionar \'Opções\'. Introdução rápida de coordenadas - Sem estradas de gelo ou vaus - Evitar estradas de gelo e vaus. + Evitar estradas de gelo ou vaus + Evitar estradas de gelo e vaus (passagens em riachos baixos). Usar posição Adicionar a sua posição como ponto de partida para planear uma rota perfeita. A minha posição @@ -1825,8 +1825,8 @@ Exporte os seus marcadores para um ficheiro que pode especificar aqui: Mover para histórico O grupo terá desaparecido na próxima vez que iniciar a aplicação. - Mostrar linhas direcionais - Mostrar setas no mapa + Mostrar linhas para marcadores + Mostrar setas para marcadores Mostrar passado Esconder o passado Remover dos \'Marcadores do mapa\' @@ -1884,7 +1884,7 @@ Ficheiros temporários dos mosaicos Nome de utilizador errado Para - A partir de + De Ver apenas imagens adicionadas Data Introduza nome de utilizador @@ -1900,15 +1900,15 @@ Instalar Melhorar cobertura fotográfica com Mapillary Instale o Mapillary para adicionar fotos a este local do mapa. - Fotos on-line - Adicionar fotos - Não há fotos aqui. - Partilhe a sua vista ao nível da rua através do Mapillary. + Fotografias na Internet + Adicionar fotografias + Não há fotografias aqui. + Partilhe as suas imagens ao nível do solo no Mapillary. Widget Mapillary Permite contribuir rapidamente para o Mapillary. - Fotos on-line ao nível da rua para todos. Descubra locais, colabore, capture o mundo. + Fotografias online ao nível da rua para todos. Descubra locais, colabore, capture o mundo. Mapillary - Fotos ao nível da rua para todos. Descubra locais, colabore, capture o mundo. + Fotografias ao nível da rua para todos. Descubra locais, colabore, capture o mundo. O seu destino está localizado numa área de acesso privado. Permitir uso de estradas privadas para esta viagem\? Reiniciar pesquisa Aumentar raio de pesquisa @@ -1925,7 +1925,7 @@ Compre e instale a extensão \'Curvas de nível\' para mostrar as áreas verticais graduadas. Esquema de cores Mostrar a partir do nível de ampliação - Permitir acesso privado + Ativar acesso a vias privadas Permitir acesso a áreas privadas. Mostrar nível de zoom: %1$s Nome do grupo @@ -2023,15 +2023,15 @@ Alto Médio Baixo - Largura das curvas de nível - Largura das curvas de nível + Espessura das curvas de nível + Espessura das curvas de nível Água - Ocultar água + Corpos de água largos Utilizar autoestradas Permitir autoestradas. Artigos da Wikipédia próximos Cidade ou região - Use a saída %1$d e continue + Saia na %1$d saída para Enviar pontos de interesse (POI) Cálculo da rota Ainda não tem nenhuns ficheiros de trilhos @@ -2046,7 +2046,7 @@ Em pausa Viagem Gravado - Registo + Gravar Sem dados Esquema de cores das curvas de nível Velocidade mínima para registo @@ -2076,10 +2076,10 @@ Eliminar filtro Novo filtro Por favor introduza o nome do novo filtro, este vai ser adicionado ao separador \'Categorias\'. - A assinatura é cobrada no período selecionado. Cancele a assinatura no Google Play a qualquer altura. + A subscrição é debitada no período selecionado. Cancele a subscrição no Google Play a qualquer altura. Doação para a comunidade OpenStreetMap - Parte da sua doação é enviada aos colaboradores do OSM. O custo da assinatura permanece o mesmo. - A subscrição permite atualizações a cada hora, diárias, semanais e descarregamentos ilimitados de todos os mapas globalmente. + Parte do seu donativo é enviado aos colaboradores do OpenStreetMap. O custo da subscrição permanece o mesmo. + A subscrição permite atualizações a cada hora, dia ou semana e descarregamentos ilimitados de todos os mapas globalmente. Obter Comprar por %1$s Obter descarregamentos ilimitados de mapas, adicionando atualizações semanais, diárias e a cada hora. @@ -2102,7 +2102,7 @@ Tem a certeza que quer substituir o favorito %1$s\? Objetos à superfície Começar - %1$s paragens antes + %1$s paragens anteriores Pesquisa de coordenadas Pesquisa avançada de coordenadas Retroceder para pesquisa @@ -2126,20 +2126,20 @@ Editar ações Adquira o OsmAnd Live para desbloquear todas as funcionalidades: atualizações diárias de mapas com descarregamentos ilimitados, todas as extensões pagas e gratuitas, Wikipédia, Wikivoyage e muito mais. Alteração do estilo padrão para aumentar o contraste de caminhos pedestres e ciclovias. Usa cores clássicas do Mapnik. - Marcador - Esconder descrição completa + Favorito + Ocultar descrição completa Mostrar a descrição completa Obrigado pelos seus comentários Procurar rua Restaurar - Os marcadores adicionados como um grupo de favoritos ou ponto de rota GPX marcados como Passado permanecerão no mapa. Se o grupo não estiver ativo, os marcadores desaparecerão do mapa. - Manter marcadores passados no mapa + Os marcadores adicionados como um grupo de favoritos ou ponto de rota GPX marcados como visitado (passado) permanecerão no mapa. Se o grupo não estiver ativo, os marcadores desaparecerão do mapa. + Manter marcadores visitados Outros transportes disponíveis nesta paragem. Por favor, conceda ao OsmAnd o acesso à localização para continuar. Eliminar o marcador de mapa \'%s\'\? Editar marcador de mapa Aplicação de terceiros - Primeiro especificar cidade/sítio/localidade + Pesquisa progressiva cidade>vila>aldeia Planos e preços Mensalmente A cada três meses @@ -2147,7 +2147,7 @@ %1$s / mês %1$.2f %2$s / mês Poupe %1$s - Assinatura atual + Subscrição atual Renova mensalmente Renova trimestralmente Renova anualmente @@ -2164,15 +2164,15 @@ Estilo de propósito geral. Renderização simplificada e limpa em cidades densamente povoadas. Características principais: curvas de nível, rotas, qualidade de superfície, restrições de acesso, escudos rodoviários, caminhos de renderização de acordo com a escala SAC, características desportivas Whitewater. Abrir página da Wikipédia na Internet A hiperligação será aberta no navegador de Internet. - Obtenha uma assinatura do OsmAnd Live para ler artigos da Wikipédia e da Wikivoyage desligado da Internet. + Obtenha uma subscrição do OsmAnd Live para ler artigos da Wikipédia e da Wikivoyage desligado da Internet. Como abrir a hiperligação\? Ler a Wikipédia desligado da Internet Descarregar tudo Reiniciar a aplicação Mostrar imagens - Cancelou a sua assinatura do OsmAnd Live - Renovar assinatura para continuar a utilizar todas as funcionalidades: - Com base nos artigos que marcou, sugerimos que descarregue os seguintes mapas: + Cancelou a sua subscrição do OsmAnd Live + Renovar a subscrição para continuar a utilizar todas as funcionalidades: + Com base nos artigos que marcou como favoritos, sugerimos que descarregue os seguintes mapas: Mapas que precisa Equipa do OsmAnd Destinos populares @@ -2187,7 +2187,7 @@ Pode e deve editar qualquer artigo no Wikivoyage. Partilhe saber, experiência, talento e a sua atenção. Começar a editar Obter acesso ilimitado - Bem-vindo ao beta aberto + Bem-vindo à versão beta aberta Guias de viagem Guias para os lugares mais interessantes do mundo dentro do OsmAnd, sem uma conexão com a Internet. Atualizações de mapas mensais @@ -2209,11 +2209,11 @@ Apenas em Wi-Fi Descarregar imagens As imagens do artigo podem ser descarregadas para uso offline. -\nSempre disponível em \'Explorar\' → \' Opções \'. +\nConfigure em \'Explorar\' → \' Opções \'. Apenas em Wi-Fi Selecione um livro de viagem Livro de viagens - Página disponível apenas on-line. Abrir no navegador da web\? + Página disponível apenas online. Abrir no navegador da web\? Cache de imagens Eliminar histórico de pesquisa Descarregar imagens @@ -2222,7 +2222,7 @@ Artigo removido Pesquisar por país, cidade ou província Ler - Artigos marcados + Artigos favoritos Explorar Conteúdo Resultado @@ -2240,17 +2240,17 @@ Digite a longitude Digite a latitude Digite a latitude e a longitude - DD°MM′SS″ - DD.DDDDDD° - DD.DDDDD° - DD°MM.MMMM′ - DD°MM.MMM′ + GG°MM′SS″ + GG.GGGGGG° + GG.GGGGG° + GG°MM.MMMM′ + GG°MM.MMM′ E O S N Nome do ponto (opcional) - Rotas próximas dentro de + Rotas próximas a menos de A menos de Digite o nome do ficheiro. Erro ao importar mapa @@ -2261,7 +2261,7 @@ Adicionar paragem inicial Mover destino para cima e criar destino Mostrar notas fechadas - Mostrar ou ocultar notas do OpenStreetMap no mapa. + Mostrar notas do OpenStreetMap. GPX - adequado para exportar para o JOSM ou outros editores do OSM. OSC - adequado para exportar para o OSM. Ficheiro GPX @@ -2280,8 +2280,8 @@ Ler o artigo completo Ler o artigo Todos os pontos do grupo - Aberto desde as - Aberto até às + Abre às + Fecha às Fecha às Abre às Abre às @@ -2292,7 +2292,7 @@ Ações Marcador Criar ou alterar objetos OSM - Criar ou alterar POIs do OSM, abrir ou comentar notas do OSM e contribuir com ficheiros de GPX gravados. + Criar ou alterar POIs do OSM, criar ou comentar notas do OSM e contribuir com ficheiros de GPX gravados. Eliminado Editado Adicionado @@ -2300,7 +2300,7 @@ Toque num marcador no mapa para movê-lo para a parte superior dos marcadores ativos sem abrir o menu de contexto. \'Um toque\' ativo Faça notas! - Adicione notas de áudio, vídeo ou foto em qualquer ponto do mapa, usando o widget ou o menu de contexto. + Adicione notas de áudio, vídeo ou fotografia em qualquer ponto do mapa, usando o widget ou o menu de contexto. Notas de áudio/vídeo por data Por data Por tipo @@ -2321,7 +2321,7 @@ Dois Um Mostrar linha direcional desde a sua posição até os locais dos marcadores ativos. - Mostrar uma ou duas setas indicando a direção para os marcadores ativos. + Mostrar uma ou duas setas indicando a direção em linha reta para os marcadores ativos. Escolher como mostrar a distância para os marcadores ativos. Especifique a quantidade de indicadores de orientação. Número de casas decimais @@ -2330,7 +2330,7 @@ Mostrar teclado numérico Colar Próximo campo - Renomear marcador + Alterar nome do marcador Um toque no mapa mostra/esconde os botões de controlo e widgets. Modo ecrã cheio Marcador visitado @@ -2350,8 +2350,8 @@ Ida e volta Tem de adicionar pelo menos um marcador para usar esta função. Não foi possível alterar a nota. - Alterar nota - Alterar nota do OSM + Alterar nota do OpenStreetMap + Alterar nota Adicionar cópia do ponto de partida como destino. Fazer viagem de ida e volta Artigos Wikivoyage mundiais @@ -2363,7 +2363,7 @@ Distrito Bairro Procurar - Aberto 24/7 + Sempre aberto (24/7) Formato de coordenadas Formato para as coordenadas geográficas. Autocarro @@ -2391,13 +2391,13 @@ Laociano Osseta Espanhol (americano) - Espanhol (Argentina) + Espanhol (argentino) Norueguês Bokmål Volapuque Tailandês Telugu - Norueguês (Nynorsk) - Neuari / Nepalbhasa + Novo norueguês + Neuari Malásio Haitiano Galego @@ -2406,7 +2406,7 @@ Asturiano Alto sorábio Cabila - Berberes + Berber Impedir a gravação autónoma Esta extensão enriquece o mapa do OsmAnd para também produzir mapas náuticos para passeios de barco, vela e outros tipos de desportos aquáticos. \n @@ -2424,7 +2424,7 @@ \nRepresenta área: %1$s x %2$s Tolerância do limite de velocidade Selecione a margem de tolerância de limite de velocidade, acima do qual receberá um aviso de voz. - O nome do favorito foi alterado para %1$s para facilitar guardar corretamente a cadeia de caracteres com emoticons num ficheiro. + O nome do favorito foi alterado para %1$s para poder guardar corretamente o texto com emoticons num ficheiro. Imprimir rota Nome de favorito duplicado O nome do favorito especificado já está a ser utilizado, foi alterado para %1$s para evitar a duplicação. @@ -2459,9 +2459,9 @@ Avançar Painel de controlo Enviar o rastreamento para um serviço web especificado, se o registo de GPX estiver ligado. - Rastreamento on-line (requer GPX) - Iniciar rastreamento on-line - Parar rastreamento on-line + Rastreamento online (requer GPX) + Iniciar rastreamento online + Parar rastreamento online Iniciar novo segmento Estradas não transitáveis Texto @@ -2487,9 +2487,9 @@ Chinês (simplificado) Chinês (Hong Kong) Chinês (tradicional) - Sem escadas + Evitar escadas Evita escadas - Sem passagens por fronteiras + Evitar passagens por fronteiras Evita cruzar fronteiras nacionais Altura máxima Especifique a altura permitida do veículo nas rotas. @@ -2504,7 +2504,7 @@ \n \nPoderá usar o navegador visual e por voz, ver POIs (pontos de interesse), criar e gerir trilhos GPX, usar (através de uma extensão) curvas de nível e dados de altitude, escolher entre os modos motorista, ciclista e pedestre, editar o OpenStreetMap e muito mais. Navegação GPS -\n• Escolha entre modos off-line (sem tarifa de roaming quando estiver no exterior) ou on-line (mais rápido) +\n• Escolha entre modos offline (sem tarifa de roaming quando estiver no exterior) ou online (mais rápido) \n• Orientação por voz passo-a-passo lhe guia ao longo do caminho (vozes gravadas e sintetizadas) \n• A rota é recalculada sempre que se desviar dela \n• Orientação de pista, nomes de ruas e tempo estimado de chegada ajudará ao longo do caminho @@ -2515,14 +2515,14 @@ \n• Suporta pontos intermédios no seu itinerário \n• Grave ou envie um trilho GPX e siga-a \n - Mapa -\n• Mostra POIs (ponto de interesse) perto de si -\n• Ajusta o mapa na sua direção de movimento (ou bússola) -\n• Mostra a sua posição e direção para onde está olhando -\n• Partilhe sua posição para que seus amigos possam encontrá-lo -\n• Mantém seus lugares mais importantes em \'Favoritos\' -\n• Permite-lhe escolher como mostrar nomes no mapa: em inglês, local ou escrita fonética -\n• Mostra mapas on-line especializados, vista de satélite (do Bing), sobreposições diferentes como trajetos GPX de navegação/turismo e camadas adicionais com transparência personalizável + Mapa +\n• Mostra POIs (ponto de interesse) perto de si +\n• Ajusta o mapa na sua direção de movimento (ou bússola) +\n• Mostra a sua posição e direção para onde está olhando +\n• Partilhe sua posição para que seus amigos possam encontrá-lo +\n• Mantém seus lugares mais importantes em \'Favoritos\' +\n• Permite-lhe escolher como mostrar nomes no mapa: em inglês, local ou escrita fonética +\n• Mostra mapas online especializados, vista de satélite (do Bing), sobreposições diferentes como trajetos GPX de navegação/turismo e camadas adicionais com transparência personalizável \n Esqui \nA extensão de mapas de esqui OsmAnd Ski permite que veja pistas de esqui com nível de complexidade e algumas informações adicionais, como localização de elevadores e outras instalações invernais. @@ -2565,7 +2565,7 @@ \n \nAlgumas das características principais: Navegação -\n• Funciona on-line (rápido) ou off-line (sem custos de roaming quando estiver no estrangeiro) +\n• Funciona online (rápido) ou offline (sem custos de roaming quando estiver no estrangeiro) \n• Orientação por voz passo a passo (vozes gravadas e sintetizadas) \n• Orientação de trajetos opcionais, visualização do nome da rua e tempo estimado de chegada \n• Suporta pontos intermédios do seu itinerário @@ -2585,7 +2585,7 @@ \n • Dados do OpenStreetMap disponíveis por país ou região \n • POIs da Wikipédia, ótimo para passeios turísticos \n • Descarregamentos grátis ilimitados, diretamente da aplicação -\n • Mapas off-line vetoriais compactos e atualizados mensalmente +\n • Mapas offline vetoriais compactos e atualizados mensalmente \n \n • Escolha entre região completa ou apenas a rede rodoviária (exemplo: o Japão inteiro tem 700 MB e a rede rodoviária tem apenas 200 MB) Recursos de segurança @@ -2598,15 +2598,15 @@ \n• Visualização de caminhos a pé, pistas de caminhadas e ciclovias, ideal para atividades ao ar livre \n• Navegação e modos de visualização especiais para bicicleta e pedestres \n• Paragens de transporte público opcionais (autocarro, elétrico, comboio) incluindo nomes de linhas -\n• Gravação opcional de viagem para ficheiro GPX local ou serviço on-line +\n• Gravação opcional de viagem para ficheiro GPX local ou serviço online \n• Visualização opcional de velocidade e altitudes \n• Visualização de curvas de nível e sombreamento de relevo (com uma extensão adicional) Contribua diretamente para o OpenStreetMap -\n • Envie relatórios de erros. -\n • Envie trilhos GPX para o OpenStreetMap diretamente da aplicação. -\n • Adicione POIs e envie-os diretamente para o OpenStreetMap (ou mais tarde se estiver desconectado da Internet). -\n • Gravação de viagem opcional também em plano de fundo (enquanto o dispositivo está no modo adormecido). -\n O OsmAnd é um programa de fonte aberta desenvolvido ativamente. Todos podem contribuir para a aplicação reportando erros, a melhorar as traduções ou a programar novas funcionalidades. Além disso, o projeto conta com contribuições financeiras para financiar a programação e testes de novas funcionalidades. +\n• Envie relatórios de erros. +\n• Envie trilhos GPX para o OpenStreetMap diretamente da aplicação. +\n• Adicione POIs e envie-os diretamente para o OpenStreetMap (ou mais tarde se estiver desconectado da Internet). +\n• Gravar viagem opcional também em plano de fundo (enquanto o dispositivo está no modo adormecido). +\n• O OsmAnd é um programa de fonte aberta desenvolvido ativamente. Todos podem contribuir para a aplicação reportando erros, a melhorar as traduções ou a programar novas funcionalidades. Além disso, o projeto conta com contribuições financeiras para financiar a programação e testes de novas funcionalidades. \n Cobertura de mapa e qualidade aproximada: \n• Europa Ocidental: **** @@ -2627,7 +2627,7 @@ Normal Atrasado Nos últimos metros - Buffer de tempo para rastreamento on-line + Buffer de tempo para rastreamento online Especificar um buffer de tempo para manter locais para enviar sem conexão Europa - Países Baixos Outros @@ -2636,7 +2636,7 @@ Adicionar favorito eliminar Reabrir - Tamanho da foto + Tamanho da fotografia Definir o tamanho da imagem Obter Recálculo de rota inteligente @@ -2657,11 +2657,11 @@ Desfazer OsmAnd Mapas e navegação -\noff-line +\noffline Enviar POI Básico Avançado - Número de edifício + Número de porta Próximo Abre às Fecha às @@ -2685,7 +2685,7 @@ %.1f MB Atualizar tudo (%1$s MB) O nome contém demasiadas letras maiúsculas. Continuar\? - Ação rápida + Botão de ação rápida Ação %d Ecrã %d Adicionar marcador de mapa @@ -2694,25 +2694,25 @@ O estilo do mapa foi alterado para \"%s\". Nova nota de áudio Nova nota de vídeo - Nova nota de foto + Nova nota de fotografia Adicionar nota OSM Ligar/desligar voz Ativar voz Desativar voz Adicionar local de estacionamento Adicionar ação - Editar ação + Editar botão de ação rápida Adicionar favorito Adicionar ação - Eliminar ação - Tem a certeza de que quer eliminar a ação \"%s\"\? + Eliminar botão de ação rápida + Tem a certeza de que quer eliminar o botão de ação rápida \"%s\"\? Mostrar lista dos favoritos Nome do modelo O botão é para adicionar um marcador de mapa no local do centro do ecrã. Um botão para adicionar um ponto de rota GPX no local do centro do ecrã. Um botão para adicionar uma nota de áudio no local do centro do ecrã. Um botão para adicionar uma nota de vídeo no local do centro do ecrã. - Um botão para adicionar uma nota de foto no local do centro do ecrã. + Um botão para adicionar uma nota de fotografia no local do centro do ecrã. Um botão para adicionar uma nota OSM no local do centro do ecrã. Um botão para adicionar um POI no local do centro do ecrã. Um botão para ativar ou desativar a orientação por voz durante a navegação. @@ -2720,8 +2720,8 @@ Mostrar uma janela temporal " guardado em " Local - O nome da ação rápida foi alterado para %1$s para evitar duplicação. - Nome de ação rápida duplicado + O nome do botão de ação rápida foi alterado para %1$s para evitar duplicação. + Nome do botão de ação rápida duplicado Uma alternância para mostrar ou ocultar os pontos favoritos no mapa. Uma alternância para mostrar ou ocultar POIs no mapa. Mostrar %1$s @@ -2754,9 +2754,9 @@ Adicionar origem do mapa A origem do mapa foi alterada para \"%s\". Mudar posição do botão - Segure e arraste o botão para mudar a sua posição no ecrã. + Pressione e segure arrastando o botão para mudar a sua posição no ecrã. Nome da ação - As fotos do Mapillary só estão disponíveis on-line. + As fotografias do Mapillary só estão disponíveis online. Repetir Adicionar pontos de rota Adicionar ponto de passagem @@ -2782,7 +2782,7 @@ Túneis Descarregar artigos da Wikipédia de %1$s para lê-los offline. Descarregar dados da Wikipédia - Abrir artigo on-line + Abrir artigo na Internet Ver artigo no navegador web. esta região A procurar o artigo wiki correspondente @@ -2791,7 +2791,7 @@ Toque num botão e ouça a mensagem de voz dele correspondente para ouvir se está ausente ou avariado Por OsmAnd Código de localização aberto (OLC) - Assinaturas + Subscrições Mostrar apenas imagens de 360° Lançar Guarani @@ -2842,7 +2842,7 @@ Não pavimentado Areia Relva - Blocos cimento perfurados + Blocos de cimento perfurados Terra Terra (lodosa) Lama @@ -2890,17 +2890,17 @@ Mostrar mais Trilhos mostrados Um botão para mostrar ou ocultar trilhos selecionados no mapa. - Sem elétricos + Evitar elétricos Evita elétricos - Sem autocarros + Evitar autocarros Evita autocarros e troleicarros - Sem táxis partilhados + Evitar táxis partilhados Evita táxis partilhados - Sem comboios + Evitar comboios Evitar comboios - Sem metropolitanos + Evitar metropolitanos Evitar metropolitanos subterrâneos e de superfície - Sem balsas/ferries + Evitar balsas/ferries Evita balsas/ferries Milirradianos Paralelos @@ -2931,23 +2931,23 @@ Tente alterar a configuração. Calcular a rota pedestre Tipo de transporte - Procurando GPS + A procurar GPS Widget de coordenadas Por favor envie os seus comentários e avalie o nosso trabalho no Google Play. Política de privacidade Ajude-nos a melhorar o OsmAnd - Permitir que OsmAnd recolha e processe dados anónimos de utilização da aplicação. Nenhuns dados sobre a sua posição, nem sobre as localizações que visualiza no mapa são recolhidos. + Permitir que OsmAnd recolha e processe dados anónimos de utilização da aplicação. Não são recolhidos nenhuns dados sobre a sua posição, nem sobre as localizações que visualiza no mapa. \n -\nConfigure a qualquer momento em \'Configurações\' → \'Privacidade e Segurança\'. +\nConfigure a qualquer momento em \'Configurações\' → \'Privacidade e segurança\'. Escolha o tipo de dados que pretende partilhar: Mapas descarregados - Ecrãs vistos - Define quais os dados que permite que OsmAnd partilhe. - Ajuda-nos a compreender a popularidade de mapas de países e regiões. - Ajuda-nos a entender a popularidade dos recursos no OsmAnd. + Ecrãs visualizados + Define que dados que permite o OsmAnd partilhar. + Permite-nos compreender a popularidade dos mapas de países e regiões. + Permite-nos entender a popularidade dos recursos no OsmAnd. Toque em \"Permitir\" se concordar com %1$s Privacidade e segurança - Escolha que dados partilha + Escolha os dados que quer partilhar Não, obrigado Permitir Nome do perfil @@ -2986,8 +2986,8 @@ Bicicleta de montanha, ciclomotor, cavalo Andar, caminhar, correr Categorias de transportes públicos - Navio, remar, navegar - Avião, asa-delta + Barco, remo, vela + Avião, parapente Geocodificação Linha reta BRouter (offline) @@ -2997,7 +2997,7 @@ Roteamento de terceiros Escolha os perfis mostrados na aplicação. Perfis da aplicação - Adicione pelo menos um item à lista nas configurações de \'Ação rápida\' + Adicione pelo menos um item à lista nas configurações de \'Botão de ações rápidas\' Esqui alpino e descendente Pistas para esqui alpino ou downhill e acesso a teleféricos de esqui. Esqui de travessia e nórdico @@ -3028,7 +3028,7 @@ Velocidade mínima Velocidade máxima Velocidade predefinida - Alterar as predefinições de velocidade + Alterar as velocidades predefinidas Definir a velocidade mínima/máxima Novo perfil Encravou @@ -3066,8 +3066,8 @@ Selecione as opções de mapa para o perfil Selecionar opções de ecrã para o perfil Selecione as configurações de navegação para o perfil - Especificar o limite máximo de alterações - Quantidade de transferências + Especificar o limite máximo de transbordos + Número de transbordos Despertar na curva Ajuste por quanto tempo o ecrã deve ficar ligado. Utilizar sensor de proximidade @@ -3097,7 +3097,7 @@ Abrir trilho O trilho %s está guardado Mostrar zonas de baixas emissões (proibida circulação de veículos poluentes) no mapa. Não afeta o roteamento. - Mostrar zonas de baixas emissões + Mostrar zonas de baixas emissões poluentes Considerar limitações temporárias Padrão Rota: distância %1$s, tempo do roteador %2$s \nCálculo: %3$.1f seg, %4$d estradas, %5$d telhas) @@ -3118,7 +3118,7 @@ Anos Três meses Grátis - Obter %1$d %2$s com %3$s de desconto. + Obtenha %1$d %2$s com %3$s de desconto. depois %1$s Cancelar subscrição %1$s - Poupe %2$s @@ -3133,11 +3133,11 @@ O formato selecionado será aplicado em toda a aplicação. Esta configuração é selecionada por padrão para os perfis: %s Alterar a configuração - Descartar alteração + Cancelar alteração Aplicar só a \"%1$s\" Aplicar a todos os perfis Mensagem de inicialização - Análises + Enviar dados anónimos Mostrar mapa no ecrã de bloqueio durante a navegação. Configurações de roteamento no perfil selecionado \"%1$s\". Tempo limite após despertar @@ -3180,7 +3180,7 @@ \n%1$s > %2$s Insira o caminho para a pasta Pasta… - Colar caminho para a pasta com dados OsmAnd + Colar caminho para a pasta com dados do OsmAnd Alterar a pasta de dados OsmAnd\? Mover para o novo destino Armazenamento interno para OsmAnd (oculto aos utilizadores e outras aplicações). @@ -3250,7 +3250,7 @@ \n - Outras correções de erros \n \n - Pode aplicar esta alteração a todos ou apenas ao perfil atualmente selecionado. + Pode aplicar esta alteração a todos os perfis ou apenas ao atualmente selecionado. Partilhado Preferir estradas não pavimentadas Preferir estradas não pavimentadas sobre as pavimentadas para o roteamento. @@ -3262,7 +3262,7 @@ %1$s • %2$s %1$s, %2$s Pessoal - Descarregando %s + A descarregar %s Grosso Para desertos e outras áreas pouco povoadas. Mais detalhado. Ícone de posição durante a movimentação @@ -3284,7 +3284,7 @@ O perfil \'%1$s\' já existe. Substituir\? Não foi possível exportar o perfil. Importar perfil - Adicione um perfil abrindo o seu ficheiro com OsmAnd. + Adicione um perfil abrindo o seu ficheiro com o OsmAnd. %1$s erro de importação: %2$s %1$s importado(s). Trocar %1$s por %2$s @@ -3293,7 +3293,7 @@ O nome do ficheiro está vazio Reverter Um botão para centrar o ecrã no ponto de partida. Em seguida, solicitará para definir o destino ou acionar o cálculo da rota. - Mostrar nós da rede de ciclovias + Nós da rede de ciclovias Limpar %1$s\? Diálogo de descarregar mapas Diálogos e notificações @@ -3352,8 +3352,8 @@ A importar dados de %1$s Tem a certeza que quer limpar os dados gravados\? Não foi possível fazer a cópia de segurança do perfil. - Gravando novo perfil - Restaurar todas as configurações de perfil\? + A guardar o novo perfil + Restaurar todas as configurações do perfil\? Todas as configurações do perfil serão restauradas para o seu estado original após a criação/importação deste perfil. Importar ficheiro de renderização Estilo de renderização @@ -3372,8 +3372,8 @@ Nada selecionado Tipos de POI A preparar - Ângulo mínimo entre minha localização e rota - Segmento reto adicional entre a minha localização e a rota calculada será mostrada até que a rota seja recalculada + Ângulo mínimo entre a minha localização e a rota + Será mostrado o segmento reto adicional entre a minha localização e a rota calculada até que a rota seja recalculada Ângulo Ângulo: %s° Perfil personalizado @@ -3393,7 +3393,7 @@ \nA extensão permanecerá no dispositivo após remover o OsmAnd. Extensão desativada Abrir configurações - Forneça um nome para o perfil + Atribua um nome ao perfil Classificar por categoria Direto ao ponto Copiar coordenadas @@ -3435,14 +3435,14 @@ Todas as configurações da extensão foram repostas para os valores de origem. Adicionar categoria personalizada Disponível - \'Restaurar valores predefinidos\' redefinirá a ordem de ordenação para os valores de origem. + \'Restaurar valores predefinidos\' irá repor a ordenação de origem. Pode adicionar uma nova categoria personalizada selecionando uma ou mais categorias. - Altere a ordem de ordenação da lista, oculte categorias. Pode importar ou exportar todas as alterações com perfis. + Altere a ordenação da lista e oculte categorias. Pode importar ou exportar todas as alterações com perfis. Reorganizar categorias Autorização bem sucedida - Som do obturador da câmara + Som ao tirar fotografias Usar aplicação do sistema - Divisão de gravação + Dividir gravações Repor configurações originais da extensão Deslocamento mínimo Precisão mínima @@ -3453,8 +3453,8 @@ Intervalo de rastreamento Memória intermédia Recomendação: uma configuração de 5 metros pode funcionar bem se não precisar capturar detalhes mais refinados do que isso e não quer capturar dados explicitamente enquanto estiver parado. - Efeitos colaterais: os períodos em que está parado não são registados em absoluto ou em apenas um ponto cada. Pequenos movimentos (do mundo real) (por exemplo, de lado, para marcar um possível desvio na sua viagem) podem ser filtrados. O seu ficheiro contém menos informações para pós-processamento e possui estatísticas piores ao filtrar pontos obviamente redundantes no tempo de gravação, mantendo potencialmente artefactos causados por má receção ou efeitos de chipset GPS. - Este filtro evita que pontos duplicados sejam gravados onde muito pouco movimento real possa ter ocorrido, cria uma aparência espacial mais agradável dos trilhos que não são processados posteriormente. + Efeitos colaterais: os períodos em que está parado não são registados em absoluto ou em apenas um ponto cada. Pequenos movimentos (no mundo real, por exemplo de lado, para marcar um possível desvio na sua viagem) podem ser filtrados. O seu ficheiro contém menos informações para pós-processamento e possui estatísticas piores ao filtrar pontos obviamente redundantes no tempo de gravação, mantendo potencialmente os artefactos causados por má receção ou efeitos do chipset GPS. + Este filtro evita que sejam gravados pontos duplicados quando houver muito pouco movimento real e cria uma aparência espacial mais agradável dos trilhos que não são processados posteriormente. Observação: se o GPS estava desligado imediatamente antes de uma gravação, o primeiro ponto medido pode ter uma precisão diminuída; portanto, no nosso código, podemos esperar um segundo antes da gravação de um ponto (ou gravar o melhor de três pontos consecutivos, etc.), mas isso ainda não foi implementado. Recomendação: é difícil prever o que será gravado e o que não será, talvez seja melhor desativar este filtro. Efeito colateral: como resultado da filtragem por precisão, os pontos podem estar totalmente ausentes por ex. debaixo de pontes, sob árvores, entre prédios altos ou com certas condições climáticas. @@ -3468,7 +3468,7 @@ Verifique e partilhe registos detalhados da aplicação Ícone mostrado quando parado. Ícone mostrado ao navegar ou mover. - Veja todas as suas edições ainda não enviadas ou bugs OSM em %1$s. Os pontos enviados não vão aparecer no OsmAnd. + Veja todas as suas edições ainda não enviadas ou notas do OpenStreetMap em %1$s. Os pontos enviados deixarão de aparecer nessa lista. Edição OSM Estas configurações da extensão são globais e aplicam-se a todos os perfis Utilizador e palavra-passe @@ -3476,12 +3476,12 @@ Recálculo da rota Notas fotográficas Notas de vídeo - Pode encontrar todas as suas notas em %1$s. + Pode encontrar todas as suas notas OSM em %1$s. Pode encontrar todos os seus trilhos gravados em %1$s ou na pasta OsmAnd. Precisão de registo Rastreamento online Permite partilhar a localização atual usando a gravação de viagem. - Escolha ícone, cor e nome + Escolha o ícone, cor e nome Utilizador, palavra-passe, edição offline Tamanho da imagem, qualidade de áudio e vídeo Navegação, precisão de registo @@ -3501,7 +3501,7 @@ Não foi possível importar de \'%1$s\'. Personalize a quantidade de itens em \"Gaveta\", \"Configurar Mapa\" e \"Menu de Contexto\". \n -\nDesative as extensões não utilizados para ocultar todos os seus controlos. %1$s. +\nDesative as extensões não utilizadas para ocultar todos os seus controlos. %1$s. Itens da gaveta, menu de contexto Personalização da interface Gaveta @@ -3525,13 +3525,13 @@ Selecione as línguas dos artigos da Wikipédia no mapa. Mude para qualquer língua disponível enquanto lê o artigo. OsmAnd + Mapillary OsmAnd Tracker - Ação rápida + Botão de ações rápidas Régua radial Medir distância Viagem (Wikivoyage e Wikipédia) - Marcadores de mapa + Marcadores Favoritos - Assinatura - OsmAnd Live + Subscrição - OsmAnd Live Compras de OsmAnd O guia para a simbologia de um mapa. Perfis de navegação @@ -3539,9 +3539,9 @@ %1$s / %2$s O pagamento será debitado na sua conta Google Play na confirmação da compra. \n -\n A assinatura é renovada automaticamente, a menos que seja cancelada antes da data de renovação. A sua conta será cobrada pelo período de renovação (mês/três meses/ano) apenas na data de renovação. +\nA subscrição é renovada automaticamente, a não ser que seja cancelada antes da data de renovação. A sua conta será cobrada pelo período de renovação (mês/três meses/ano) apenas na data de renovação. \n -\n Pode gerir e cancelar as suas subscrições, indo às suas definições do Google Play. +\nPode gerir e cancelar as suas subscrições, indo às suas definições do Google Play. Ação %1$s não suportada Mapas adicionais Alguns artigos da Wikipédia podem não estar disponíveis no seu idioma. @@ -3566,7 +3566,7 @@ Cazaque Javanês Guzerate - Tchuvache + Chuvache Checheno Bávaro Basquires @@ -3604,9 +3604,9 @@ Projeção pseudo-Mercator Um ficheiro de imagem por mosaico Ficheiro SQLiteDB - Forneça um nome para a origem do mapa on-line. - Insira ou cole a URL para a origem on-line. - Editar origem on-line + Forneça um nome para a origem do mapa online. + Introduza ou cole o URL para a origem online. + Editar origem online Tempo de validade Projeção de Mercator Formato de armazenamento @@ -3638,7 +3638,7 @@ Kart Os alertas de radares de velocidade em alguns países são proibidos por lei. Manter ativo - Forneça um nome para o ponto + Atribua um nome ao ponto A aplicação dessas alterações limpará os dados temporários desta origem de mosaicos Este dispositivo não tem instalado o recurso de câmaras de velocidade. Em alguns países ou regiões, o uso de aplicações com avisos de radares é proibido por lei. @@ -3647,18 +3647,18 @@ \n \nSelecione %1$s e receberá alertas e avisos sobre radares de velocidade. \n -\nSelecione %2$s. Todos os dados relacionados a radares de velocidade: alertas, notificações, POIs serão apagados até que o OsmAnd seja completamente reinstalado. - Obter informações sobre pontos de interesse da Wikipédia. Um guia de bolso off-line para ver artigos sobre locais e destinos. +\nSelecione %2$s e yodos os dados relacionados a radares de velocidade: alertas, notificações, POIs serão eliminados até que o OsmAnd seja completamente reinstalado. + Obter informações sobre pontos de interesse da Wikipédia. Um guia de bolso offline para ver artigos sobre locais e destinos. Scooter Eliminar o ponto de destino mais próximo Descarregar mapas da Wikipédia Pode aceder a essas ações tocando no botão “%1$s”. Desinstalar Forneça o comprimento do seu veículo, podem ser aplicadas algumas restrições de rota a veículos longos. - Botões de volume como zoom + Botões de volume mudam o zoom Reinicie a aplicação para eliminar os dados da câmara de velocidade. %1$s eliminado - Se tocar neste botão de ação, mostra ou oculta a camada Mapillary no mapa. + Um botão que alterna entre mostrar ou ocultar a camada Mapillary no mapa. Definir altura da embarcação Motorizada de enduro Patins em linha @@ -3701,10 +3701,10 @@ Defina dias úteis para continuar Rota entre pontos Planear uma rota - Adicionar ao trilho + Adicionar a um trilho O ponto adicionado não será visível no mapa, já que o grupo selecionado está escondido, pode encontrá-lo em \"%s\". Mostrar ícones de início e fim - Selecionar a largura + Espessura da linha do trilho Selecione o intervalo em que as marcas com distância ou tempo no trilho serão mostradas. Selecione a opção de divisão desejada: por tempo ou por distância. Personalizado @@ -3715,7 +3715,7 @@ Abrir trilho existente Selecione um ficheiro de trilho para abrir. Criar nova rota - Pronto + Feito Substituir trilho Guardar como novo trilho Rota inversa @@ -3740,8 +3740,8 @@ Eliminar endereço Adicionar endereço Insira o endereço - Aparecer antes - Aparecer depois + Recortar antes + Recortar depois Alterar o tipo de rota antes Alterar o tipo de rota após Imagens ao nível das ruas @@ -3759,9 +3759,9 @@ Registar o trilho num ficheiro GPX Trilhos Adicionar ponto de passagem de trilho - Gravação de viagem + Gravar viagem Gravar - Especifique o intervalo de registo para a gravação geral do trilho (ligado através do widget de \'gravação de viagem\' no mapa). + Especifique o intervalo de registo para a gravação geral do trilho (ligado através do widget de \'Gravar viagem\' no mapa). Pausar a gravação da viagem Retomar a gravação da viagem Predefinição do sistema @@ -3770,7 +3770,7 @@ Todos os segmentos anteriores Apenas o segmento selecionado será recalculado a usar o perfil selecionado. Todos os segmentos subsequentes serão recalculados a utilizar o perfil selecionado. - Todos os segmentos anteriores serão recalculados a usar o perfil selecionado. + Todos os segmentos anteriores serão recalculados usando o perfil selecionado. Abrir trilho guardado está guardado Adicione pelo menos dois pontos. @@ -3800,14 +3800,14 @@ Nome: A – Z Ícones de início e fim Obrigado por comprar \'Curvas de nível\' - A assinatura é cobrada pelo período selecionado. Cancele-a na sua AppGallery a qualquer momento. + A subscrição é cobrada pelo período selecionado. Cancele-a na sua AppGallery a qualquer momento. Ao confirmar a sua subscrição, será debitado na sua conta AppGallery. \n -\nA assinatura é renovada automaticamente, a não ser que seja cancelada antes da data de renovação. A sua conta será cobrada pelo período de renovação (mês/três meses/ano) apenas na data de renovação. +\nA subscrição é renovada automaticamente, a não ser que seja cancelada antes da data de renovação. A sua conta será cobrada pelo período de renovação (mês/três meses/ano) apenas na data de renovação. \n -\nPode gerir e cancelar as suas subscrições a ir às definições da sua AppGallery. - Evitar passeios - Evitar passeios +\nPode gerir e cancelar as suas subscrições nas definições da sua AppGallery. + Evitar vias pedonais + Evitar vias pedonais Novidades Desenvolvimento Dados do OsmAnd Live @@ -3818,12 +3818,12 @@ Inicie sessão com o OAuth para usar os recursos de edição OpenStreetMap Iniciar sessão com OAuth Limpar token do OpenStreetMap OAuth - Saiu + Sessão terminada O ficheiro já foi importado para o OsmAnd Usar algoritmo de roteamento de 2 fases A* Para a condução de motos de neve com estradas e pistas dedicadas. Gráfico - Dados de %1$s disponíveis apenas em estradas, calcule uma rota a usar \"Rota entre pontos\" para ver os gráficos. + Dados de %1$s disponíveis apenas em estradas, calcule uma rota usando \"Rota entre pontos\" para ver os gráficos. Por favor aguarde. \nO gráfico estará disponível após o recálculo da rota. %1$s — %2$s @@ -3841,34 +3841,34 @@ Emergência Viagem Adicione pelo menos dois pontos - Gerir a assinatura - Toque no botão para ir às definições de assinatura do Google Play para corrigir a sua assinatura. - A assinatura do OsmAnd Live expirou - A assinatura do OsmAnd Live foi pausada - A assinatura do OsmAnd Live está em espera + Gerir a subscrição + Toque no botão para ir às definições de assinatura do Google Play para corrigir a sua subscrição. + A subscrição do OsmAnd Live expirou + A subscrição do OsmAnd Live foi pausada + A subscrição do OsmAnd Live está em espera Tem de iniciar sessão para enviar alterações. \n \nPode iniciar sessão usando o método seguro OAuth ou usar o seu nome de utilizador e a sua palavra-passe. Iniciar sessão no OpenStreetMap Iniciar sessão no OpenStreetMap.org - Entrar com OpenStreetMap + Iniciar sessão com OpenStreetMap Usar o nome do utilizador e palavra-passe Conta Utilizador Histórico de marcadores Enviar ficheiro de GPX para o OpenStreetMap Introduza etiquetas separadas por vírgula. - \"Público\" significa que o rastreamento é mostrado publicamente em Seus traços de GPS e em listas públicas de rastreamento de GPS, e na lista pública de rastreamento com carimbos de data e hora na forma bruta. Os dados servidos através da API não fazem referência à sua página de rastreamento. Os marcadores de ponto de rastreamento não estão disponíveis através da API de GPS pública, e os pontos de rastreamento não estão ordenados cronologicamente. + \"Público\" significa que o rastreamento é mostrado publicamente nos seus traços de GPS, em listas públicas de rastreamento de GPS e na lista pública de rastreamento com carimbos de data e hora na forma bruta. Os dados servidos através da API não fazem referência à sua página de rastreamento. Os marcadores de ponto de rastreamento não estão disponíveis através da API de GPS pública, e os pontos de rastreamento não estão ordenados cronologicamente. \"Privado\" significa que o rastreamento não aparece em nenhuma listagem pública, mas os pontos de rastreamento dela em ordem não cronológica estão disponíveis através da API pública do GPS sem carimbos de tempo. \"Identificável\" significa que o rastreamento será mostrado publicamente nos seus traços de GPS e nas listas públicas de rastreamento de GPS, ou seja, outros utilizadores serão capazes de descarregar o rastreamento bruto e associá-lo ao seu nome de utilizador. Os dados públicos de pontos de rastreamento marcados com a hora da API do GPS servidos através da API de pontos de rastreamento farão referência à sua página de rastreamento original. - \"Rastreável\" significa que o rastreamento não aparece em nenhuma listagem pública, mas pontos de rastreamento processados com carimbos de tempo a partir dele (que não podem ser associados diretamente a si) fazem através de descarregadas da API pública do GPS. + \"Rastreável\" significa que o rastreamento não aparece em nenhuma listagem pública, mas pontos de rastreamento processados com carimbos de tempo a partir dele (que não podem ser associados diretamente a si) estarão disponíveis através da API pública de GPS. Fechar nota do OSM Comentário de nota do OSM Pode iniciar sessão pelo método seguro OAuth ou use o seu nome de utilizador e a palavra-passe. - Adicionar fotos - Cadastre-se em + Adicionar fotografia + Crie uma conta em \nOpenPlaceReviews.org - As fotos são fornecidas pelo projeto de dados abertos OpenPlaceReviews.org. Para enviar as suas fotos precisa cadastrar-se no site. + As fotografias são fornecidas pelo projeto de dados abertos OpenPlaceReviews.org. Para enviar as suas fotografias tem de criar uma conta nesse site. Criar uma conta Já tenho uma conta Histórico de pesquisa @@ -3878,14 +3878,14 @@ Tamanho aproximado do ficheiro Selecione os dados a serem exportados para o ficheiro. Necessário para a importação - O seu dispositivo só tem %1$s livres. Por favor, liberte algum espaço ou desmarque alguns itens para exportar. + O seu dispositivo só tem disponível %1$s. Liberte algum espaço ou desmarque alguns itens para exportar. Não há espaço suficiente Adicionar ao Mapillary Adicionar ao OpenPlaceReviews - OsmAnd mostra fotos de várias origens: -\nOpenPlaceReviews - fotos de POI; -\nMapillary - imagens ao nível da rua; -\nWeb / Wikimedia - fotos de POI como nos dados do OpenStreetMap. + OsmAnd mostra fotografias de várias origens: +\nOpenPlaceReviews - fotografias de POI; +\nMapillary - fotografias ao nível do solo; +\nWeb / Wikimedia - fotografias de POI definidas no OpenStreetMap. Selecionar os grupos que serão importados. Selecionar os objetos que serão importados. Utilizar dev.openstreetmap.org @@ -3894,7 +3894,7 @@ Mude para usar dev.openstreetmap.org ao invés de openstreetmap.org para testar enviar uma OSM Nota / POI / GPX. %1$s * %2$s Alemão (casual) - Aeronaves ligeiras + Aeronave ligeira Unir segmentos Dividir antes Dividir após @@ -3915,10 +3915,10 @@ URL do servidor Digite o parâmetro Mantenha-o vazio se não - O URL com todos os parâmetros parecerá-se assim: - Cálculo da rota de teste + O URL com todos os parâmetros será assim: + Testar cálculo de rota Condução - + A pé Bicicleta Carro Erro, verifique novamente os parâmetros @@ -3932,12 +3932,12 @@ Analisar por intervalos Enviar para OpenStreetMap Editar trilho - Renomear trilho + Alterar nome do trilho Mudar pasta Sombras de relevo / declives / curvas de nível - OpenPlaceReviews é um projeto comunitário sobre lugares públicos como restaurantes, hotéis, museus, pontos de passagem. Recolhe toda a informação pública sobre eles, como fotos, resenhas, ligações para outros sistemas OpenStreetMap, Wikipédia. + OpenPlaceReviews é um projeto comunitário sobre lugares públicos como restaurantes, hotéis, museus, locais etc. Recolhe toda a informação pública sobre eles, como fotografias, classificações/avaliações, ligações para outros sistemas como o OpenStreetMap e a Wikipédia. \n -\nTodos os dados do OpenPlaceReviews estão abertos e disponíveis para todos: http://openplacereviews.org/data. +\nTodos os dados do OpenPlaceReviews estão abertos e disponíveis para todos: http://openplacereviews.org/data \n \nPode ler mais em: http://openplacereviews.org OpenPlaceReviews @@ -3960,13 +3960,17 @@ Bicicleta elétrica As atualizações ao mapa serão verificadas a todas as horas. Próxima %1$s em %2$s. Tem a certeza que quer eliminar todas as %s atualizações OsmAnd Live\? - • Atualizações OsmAnd Live movidas para \"Descarregamentos > Atualizações\" + • Adicionada opção para descarregar curvas de nível em pés. \n -\n • Os trilhos podem ser agora coloridos conforme a altitude, velocidade e declive. +\n• Planear rota: adicionadas abas para alternar entre pontos ou gráficos. \n -\n • Adicionada opção para alterar a aparência da linha de rota de navegação +\n• Atualizações OsmAnd Live movidas para \"Descarregamentos > Atualizações\" \n -\n • Janela de diálogo \"Gravação do trilho\" atualizada +\n• Os trilhos podem ser agora coloridos conforme a altitude, velocidade e declive. +\n +\n• Adicionada opção para alterar a aparência da linha de rota de navegação. +\n +\n• Janela de diálogo \"Gravação do trilho\" atualizada. \n \n O roteamento pode evitar subidas íngremes. @@ -4021,11 +4025,11 @@ Última verificação: %s Frequência da atualização Necessário reiniciar a aplicação para aplicar algumas configurações. - Período de tempo do intervalo de registo em que o OsmAnd perguntará pelos dados da posição atual. + Frequência em que o OsmAnd irá registar a localização caso o sinal GPS esteja disponível. Não tem compras Se tiver alguma dúvida, contacte-nos em %1$s. Intervalos de tempo e distância - Distância por toque + Distância com 2 dedos Contacte o suporte Por favor siga este link se tiver algum problema com assinaturas. Atualizar todos os mapas para %1$s\? @@ -4039,7 +4043,7 @@ Permitir ribeiros e valas Perfil OsmAnd Perfil do utilizador - Permitir vias aquáticas intermitentes + Permitir cursos de água intermitentes Bicicleta de corrida BTT Segmento %1$d @@ -4075,18 +4079,22 @@ Especificar cor para o modo mapa: %1$s. Novo dispositivo / nova conta Resolução de problemas - Assinatura anual - Assinatura mensal - Assinatura de 3 meses + Subscrição anual + Subscrição mensal + Subscrição de 3 meses Próxima faturação: %1$s Em período experimental Em espera - Permitir hidrovias sazonais + Permitir cursos de água sazonais Cancelada - Renovar assinatura + Renovar subscrição Expirou Número da saída Anunciar quando ultrapassado Pontos do utilizador Saída + pés + O OsmAnd fornece dados de curvas de nível em metros e pés. Terá de descarregar novamente o ficheiro para alterar o formato. + Formato de unidades de curvas de nível + Selecione o formato necessário. Terá de descarregar novamente o ficheiro para alterar o formato. \ No newline at end of file diff --git a/OsmAnd/res/values-ru/strings.xml b/OsmAnd/res/values-ru/strings.xml index dc63a9788a..726ed7bd3d 100644 --- a/OsmAnd/res/values-ru/strings.xml +++ b/OsmAnd/res/values-ru/strings.xml @@ -141,8 +141,8 @@ Использовать данные о высотах Действие переименовано в %1$s, чтобы избежать дублирования. Обнаружен дубликат имени - Переключатель, чтобы показать или скрыть избранные точки на карте. - Переключатель, чтобы показать или скрыть POI на карте. + Переключатель для отображения или скрытия избранных точек на карте. + Переключатель для отображения или скрытия POI на карте. Категория Действия Если оставить это поле пустым, то оно будет автоматически заполнено адресом или названием места. @@ -3660,7 +3660,7 @@ Вы можете указать высоту судна, чтобы избегать низких мостов. Имейте в виду, что если мост раздвижной, будет использована его высота в открытом состоянии. Укажите высоту судна, чтобы избежать низких мостов. Имейте в виду, что если мост раздвижной, будет использована его высота в открытом состоянии. Укажите ширину судна, чтобы избежать узких мостов - Переключатель, чтобы показать или скрыть слой Mapillary на карте. + Переключатель для отображения или скрытия слоя Mapillary на карте. Законодательство В некоторых странах и регионах использование предупреждений о камерах контроля скорости запрещено законом. \n @@ -4073,7 +4073,7 @@ Обновить все карты, добавленные в %1$s\? Настройка линии маршрута Линия маршрута - Линия маршрута будет использовать %1$s, указанный в выбранном стиле карты: %2$s. + Линия маршрута будет использовать %1$s из выбранного стиля карты: %2$s. Укажите цвет для режима карты: %1$s. У вас нет покупок Новое устройство / новый аккаунт diff --git a/OsmAnd/res/values-sc/phrases.xml b/OsmAnd/res/values-sc/phrases.xml index ce6674505d..c3891f8100 100644 --- a/OsmAnd/res/values-sc/phrases.xml +++ b/OsmAnd/res/values-sc/phrases.xml @@ -3894,4 +3894,38 @@ Riferimentu locale Geodesista Tzentru pro cunferèntzias + Wakeboard + Ultimate + Pista pro mototziclismu + Tiru de ferros de caddu + Polo in bitzicleta + Curling + Crossfit + Lutas de puddos + Imbèrghidas dae sos iscameddos + Bob + Biathlon + Iscì de abba + Polo de abba + Zurkhaneh + Luta + Artziada de pesos + Ufìtziu diplomàticu + Kickboxing + Ischerma + Corrida + Aikido + Taekwondo + Biliardinu + Sumo + Snooker + Tiru de su pesu + Pilates + Jiu-jitsu + Karatè + Canisteddos + Pratzola de acampamentu + Casta de baia + Tzìrculu sotziale + Artipranu \ No newline at end of file diff --git a/OsmAnd/res/values-sc/strings.xml b/OsmAnd/res/values-sc/strings.xml index c11864da5a..d5eef9a6b6 100644 --- a/OsmAnd/res/values-sc/strings.xml +++ b/OsmAnd/res/values-sc/strings.xml @@ -1769,7 +1769,7 @@ Imbia PDI OSM modificadu PDI OSM iscantzelladu - Copiadu in sos apuntos + Copiadu in punta de billete Sarva in sa memòria (impreu chene lìnia) Colore GPX Largària GPX @@ -4046,4 +4046,41 @@ Totu sos datos non sarvados s\'ant a pèrdere. Ammustra su diàlogu de incumintzu Dislinda unu colore pro sa modalidade pro sa mapa: %1$s. + • Agiornamentos de OsmAnd Live (OSmAnd in direta) tramudados cara a \"Iscarrigamentos > Agiornamentos\" +\n +\n • Sas rastas como si podent colorizare pro artària, lestresa, o pendèntzia. +\n +\n • Annanghidura de s\'optzione pro mudare s\'aparèntzia de sa lìnia de navigatzione de s\'àndala +\n +\n • Agiornamentu de sa ventanedda de diàlogu \"Registratzione de su biàgiu\" +\n +\n + In perìodu de gràtzia + OsmAnd in direta + Divàriu + S\'intervallu de registratzione impostat sa frecuèntzia cun sa cale OsmAnd at a pedire sos datos de sa positzione atuale. + Si est disabilitadu sa registratzione at a incumintzare a pustis de su tocu in subra de su widget o de s\'elementu de su menù, brinchende sa ventanedda de diàlogu de cunfirma. + Personaliza sa lìnia de s\'àndala + Lìnia de s\'àndala + Pro sa lìnia de s\'àndala s\'at a impreare su %1$s dislindadu in s\'istile de sa mapa ischertadu: %2$s. + Non tenes còmpora peruna + Dispositivu nou / contu nou + Si tenes preguntas cuntata·nos a su %1$s. + Si sa còmpora tua non benit ammustrada inoghe toca in “%1$s” o cuntata su grupu de suportu nostru. + Cuntata su suportu + Risolutzione de problemas + Si tenes problemas cun sa còmpora sighi custu ligàmene, pro praghere. + Abbonamentu annuale + Abbonamentu mensile + Abbonamentu trimestrale + Data de addèbitu imbeniente: %1$s + Annulladu + Rinnova s\'abbonamentu + In isetu + Iscadidu + Agiornare totu sas mapas annànghidas a %1$s\? + Nùmeru de essida + Avisa in casu de barigamentu + Puntos de s\'impreadore + Essida \ No newline at end of file diff --git a/OsmAnd/res/values-sk/strings.xml b/OsmAnd/res/values-sk/strings.xml index b644da49d4..77dc42838a 100644 --- a/OsmAnd/res/values-sk/strings.xml +++ b/OsmAnd/res/values-sk/strings.xml @@ -1494,7 +1494,7 @@ Naozaj chcete odoslať %1$d zmien do OSM\? Vymazať históriu? prekročený čas - Vo viacerých krajinách (ako Nemecko, Francúzsko, Taliansko) je použitie varovaní pred rýchlostnými radarmi/kamerami zakázané zákonom (nelegálne). OsmAnd nepreberá žiadnu zodpovednosť ak porušíte zákon. Stlačte \"Áno\" len ak ste na to oprávnený použiť túto funkciu. + Vo viacerých krajinách (ako Nemecko, Francúzsko, Taliansko) je použitie varovaní pred rýchlostnými radarmi/kamerami zakázané zákonom. OsmAnd nepreberá žiadnu zodpovednosť ak porušíte zákon. Stlačte \"Áno\" len ak ste oprávnený použiť túto funkciu. Stiahnuť mapy Vyberte svoj región, aby bolo možné správne nastaviť dopravné predpisy: OsmAnd poskytuje celosvetové offline mapy a offline navigáciu. @@ -2729,7 +2729,7 @@ Wikipédia offline Vrstevnice & tieňované svahy Odomknite všetky funkcie OsmAnd - Stiahnite články Wikipédie pre %1$s pre ich čítanie offline. + Stiahnite články Wikipédie pre %1$s na ich čítanie offline. Stiahnuť údaje Wikipédie Otvoriť článok online Zobraziť článok vo webovom prehliadači. @@ -4006,7 +4006,7 @@ Zvoľte účel jazdy pre získanie kratšej, rýchlejšej alebo bezpečnejšej trasy Neotáčať mapu, ak je rýchlosť nižšia ako hranica Reštartovať - Všetky regióny + Všetky oblasti Zmazať %1$d súborov\? Zastaviť bez uloženia Uložiť a zastaviť záznam diff --git a/OsmAnd/res/values-sr/phrases.xml b/OsmAnd/res/values-sr/phrases.xml index fae32d16dc..247a5f4aea 100644 --- a/OsmAnd/res/values-sr/phrases.xml +++ b/OsmAnd/res/values-sr/phrases.xml @@ -3867,4 +3867,45 @@ Река Цепљење: Ковид 19 Цепљење + Друштвени клуб + Плато + Дипломатско представништво + Кик-бокс + Мачевање + Курлинг + Кросфит + Туча петлова + Борба са биковима + Боб + Биатлон + Аикидо + Водено скијање + Ватерполо + Рвање + Дизање тегова + Вејкбординг + Ултимат + Таеквондо + Футсал + Сумо + Снукер + Стање стазе: отворена + Обилажена: не + Обилажена: да + Име стазе + Ски скок + Тунел за слепе мишеве + Мост за слепе мишеве + Прелаз за дивљач + Део за пливање + Појило + Станица за пренос отпада + Колска вага + Шумарска станица + Цевни бунар + Бунар + Електрична пумпа + Танк са водом + Чесма + Тип залива \ No newline at end of file diff --git a/OsmAnd/res/values-sr/strings.xml b/OsmAnd/res/values-sr/strings.xml index f3e3dd9e2b..e5e9e86e8d 100644 --- a/OsmAnd/res/values-sr/strings.xml +++ b/OsmAnd/res/values-sr/strings.xml @@ -662,7 +662,7 @@ ОСМ измене дељене преко OsmAnd-а OSM тачка од интереса је направљена nmi - kn + чв min/m min/km m/s @@ -922,7 +922,7 @@ Образац боја путева Омогућите додатак за „снимање путовања“ ради коришћења услуга бележења (GPX бележење, праћење положаја на мрежи) Рачунај могућу приближну путању за велике раздаљине - Омогућите GPS у поставкама + Омогућите GPS у подешавањима Услуге бележења путање Нема пута Стигли сте на пролазно одредиште @@ -1005,7 +1005,7 @@ Молимо, унесите име новог филтера који ће бити придодат језичку „Категорије“. Чланарина се наплаћује по одабраном периоду. Можете је отказати на Гугл плеју кад год пожелите. Прилог ОСМ заједници - Део Вашег прилога ће бити послат корисницима ОСМ-а. Чланарина остаје иста. + Део Ваше чланарине ће бити послат корисницима ОСМ-а. Чланарина остаје иста. Чланарина омогућава часовне, дневне и седмичне надоградње, и неограничена преузимања свих карата. Добавите је Добавите за %1$s @@ -1062,9 +1062,9 @@ Указуј на правац циљне тачке звуком. Упутства трешењем Указуј на правац циљне тачке трешњом. - Омогући навођење живих измена ОСМ-а. + Омогући навођење измена ОСМ-а уживо. Навођење уживо ОСМ-а - Одредиште није подешено + Додатак за приступачност: Одредиште није подешено Изаберите категорију Пребачено на интерну меморију пошто је означено складиште за податке заштићено од писања. Изаберите фасциклу за складиште у коју може да се пише. Наведите интервал логовања снимања путање приликом навођења @@ -1221,7 +1221,7 @@ Коњске стазе положај: Бретонски - Преузмите слој са сенчењем да бисте видели рељеф на карти. + Преузмите карту слоја са сенчењем да бисте видели рељеф на карти. Инсталирајте додатак \"Изохипсе\" да прикажете нагиб вертикалних области. Додатак Приказ нивоа увеличања: %1$s @@ -1234,11 +1234,11 @@ Додај слике Нема слика овде. Поделите Ваш поглед са улице преко Мапилара. - Справица Мапилара - Омогућава брзи допринос Мапилару. - Отвори Мапилар + Mapillary справица + Омогућава брзи допринос Mapillary-ју. + Отвори Mapillary Мрежне слике улица за све. Откријте места, сарађујте, освојите свет. - Мапилари + Mapillary Мрежне слике улица за све. Откријте места, сарађујте, освојите свет. Ваше одредиште се налази на приватном поседу. Дозволити коришћење приватних путева на овом путовању\? Препокрени претрагу @@ -1512,8 +1512,8 @@ Не могу да увезем фајл. Проверите да ли OsmAnd има дозволе за читање фајла. Растојање је исправљено Слика са Мапилара - Побољшајте покривеност слика користећи Мапилар - Инсталирајте програм Мапилар (Mapillary) да додате слике на ову локацију на карти. + Побољшајте покривеност слика користећи Mapillary + Инсталирајте програм Mapillary да додате слике на ову локацију на карти. Претплатите се на нашу дописну листу за попуст и добијте још 3 преузимања карти! Изобате мора (изохипсе дубине) и карте поморских ознака. Хвала Вам на куповини „Поморских изобата“ @@ -1861,7 +1861,7 @@ Помери дугме Дуго држање и превлачење дугмета га помера по екрану. Име радње - Слике са Мапилара је могуће видети само ако сте повезани на интернет. + Слике са Mapillary-ја је могуће видети само ако сте повезани на интернет. Покушај поново Додај омиљене Увезите Омиљене тачке или их додајте означавајући их као ознаке на карти. @@ -2134,7 +2134,7 @@ Морате имати интернет да бисте инсталирали овај додатак. Преузми Пмаетно прерачунавање пута - Прерачунава само почетни део руте. Може се користити за дуга путовања. + Прерачунава само почетни део руте, корисно за дуга путовања. Да ли Вам се OsmAnd свиђа? Важно нам је да чујемо Ваше мишљење. Оцените ову апликацију @@ -2543,7 +2543,7 @@ Не могу да ажурирам локални списак тачака од интереса. Дугме за додавање GPX пролазне тачке на средину екрана. Прикажи слике - Укинули сте чланарину за OsmAnd Live + Отказали сте чланарину за OsmAnd уживо Обновите чланарину да наставите да користите све ове функционалности: Може да се користи за идентификацију Може да се користи за праћење @@ -2581,7 +2581,7 @@ Стил опште намене. Густи градови су приказани јасно. Приказује изохипсе, путање, квалитет подлоге, забране приступа, блокаде путева, исцртавање путева по SAC алпској скали, објекти за спортове на брзацима. Отвори Википедија везу са интернетом Веза ће бити отворена у веб читачу. - Да бисте читали чланке са Википедије и Wikivoyage-а, претплатите се на OsmAnd Live. + Да бисте читали чланке са Википедије и Wikivoyage-а, купите чланарину на OsmAnd уживо. Како да отворим везу? Читај Википедију ван мреже Преузми све @@ -2703,7 +2703,7 @@ \n - Окретањем мапе према компасу или правцу кретања \n - Навођењем у праву траку, приказ ограничења брзине, снимљени и синтетизовани гласови за навођење \n - Набавите OsmAnd Live да откључате ове могућности: дневна ажурирања карти са неограниченим бројем скидања, сви и плаћени и бесплатни додаци, Википедија, Wikivoyage и још много тога. + Набавите OsmAnd уживо да откључате ове могућности: дневна ажурирања карти са неограниченим бројем скидања, сви и плаћени и бесплатни додаци, Википедија, Wikivoyage и још много тога. Карта \n • Приказ тачака од интереса око Вас \n • Подешавање карте према правцу кретања (или компасу) @@ -2777,14 +2777,14 @@ %1$s / месечно %1$.2f %2$s / месечно Уштедите %1$s - Тренутна претплата + Тренутна чланарина Месечно обнављање Квартално обнављање Годишње обнављање %1$.2f %2$s Период плаћања: Донације помажу финансирање OSM картографа. - Претплате + Чланарине Прикажи само слике од 360° Покрени Гварани @@ -2894,8 +2894,8 @@ Приказане путање Укрцавање на стајању Дугме које приказује или сакрива одабране путање са карте. - Омогући јавни превоз на OsmAnd Live изменама. - OsmAnd Live јавни превоз + Омогући јавни превоз на OsmAnd уживо изменама. + OsmAnd уживо јавни превоз Калдрма Поплочано камење Ауто-пут @@ -3119,7 +3119,7 @@ %1$s првих %2$s %1$s првих %2$s онда %1$s - Поништи претплату + Поништи чланарину %1$s • Уштеди %2$s Поставке за профил: OsmAnd користи UTM Standard format који је сличан, али није истоветан као UTM NATO format. @@ -3330,7 +3330,7 @@ Корисничко име и лозинка Ова подешавања додатака су глобална и примењују се на све профиле OSM уређивање - Све своје још увек не отпремљене измене или ОСМ белешке можете погледати у %1$s. Отпремљене тачке се не приказују у ОсмАнду. + Погледајте све Ваше још увек неотпремљене измене или ОСМ белешке у %1$s. Отпремљене тачке се не више приказују. OSM Иконица која се приказује за време навођења или померања. Иконица која се приказује у мировању. @@ -3484,7 +3484,7 @@ Измери удаљеност Путовање (Wikivoyage и Википедија) Омиљени - Претплата - OsmAnd уживо + Чланарина - OsmAnd уживо OsmAnd куповине Упутство за легенду карте. Профили навођења @@ -3650,9 +3650,9 @@ Акције Контекст менија Наплатом ће бити оптерећен ваш Гугл Плеј налог при потврди куповине. \n -\n Претплата се аутоматски обнавља уколико није отказана пре датума обнове. Ваш налог биће задужен периодом обнове (месец / три месеца / годину дана) само на дан обнове. +\nЧланарина се аутоматски обнавља уколико није отказана пре датума обнове. Ваш налог биће задужен периодом обнове (месец / три месеца / годину дана) само на дан обнове. \n -\n Претплатама можете управљати и отказати их тако што ћете отићи на ваша Гугл Плеј подешавања. +\nЧланаринама можете управљати и отказати их тако што ћете отићи на ваша Гугл Плеј подешавања. • Нове офлајн мапе нагиба \n \n • Пуно прилагођавање Фаворита и ГПКС тачака – прилагођавање боја, икона, облика @@ -3683,9 +3683,9 @@ Унесите дужину возила, нека ограничења пута могу бити примењена за дужа возила. Обриши најближу одредишну тачку Молимо одредите име тачке - Тренутна одредишна тачка биће уклоњена. Ако је она одредишна, стопираће се навигација. + Уклања тренутну одредишну тачку. Ако је она и завршна тачка, навођење ће се зауставити. Преузмите мапе Википедије - Информације о тачкама од интереса потражите на Википедији. То је ваш џепни ванмрежни водич - само укључите додатак Википедија и уживајте у чланцима о објектима око вас. + Информације о тачкама од интереса потражите на Википедији, џепном ванмрежном водичу који има чланке о местима и одредиштима. Ендуро скутер Скутер Инвалидска колица @@ -3723,7 +3723,7 @@ Навигацијски профил Изаберите датотеку записа којој ће се додати нови сегмент. Слике на нивоу улице - Да ли сте сигурни да желите да одбаците све промене на планираној рути затварањем\? + Да ли сте сигурни да желите да одбаците све промене на планираној рути\? У случају обрнутог правца Сачувај као нову датотеку стазе Додај у датотеку стазе @@ -3761,9 +3761,9 @@ Паузирај снимање пута Додајте бар две тачке. Одјављен - „Приватно“ значи да се траг не појављује ни на једној јавној листи, али су тачке праћења у њему у нехронолошком редоследу доступне путем јавног ГПС АПИ-ја без временских ознака. - „Могуће је идентификовати“ значи да ће се траг јавно приказати у вашим ГПС траговима и у јавним списковима ГПС трагова, тј. други корисници ће моћи да преузму необрађени траг и повежу га са вашим корисничким именом. Јавни подаци о временским тачкама трага из ГПС АПИ-ја који се сервирају путем АПИ-ја за тачке праћења имаће референцу на вашу оригиналну страницу праћења. - „Следљиво“ значи да се траг не приказује нигде на јавним листама, али обрађене тачке праћења са временским ознакама у њима (које не могу бити директно повезане са вама) иду кроз преузимања са јавног ГПС АПИ-ја. + „Приватно“ значи да се траг не појављује ни на једној јавној листи, али су тачке праћења у њему у нехронолошком редоследу доступне путем јавног GPS API-ја без временских ознака. + „Могуће је идентификовати“ значи да ће се траг јавно приказати у Вашим GPS траговима и у јавним списковима GPS трагова, тј. други корисници ће моћи да преузму необрађени траг и повежу га са Вашим корисничким именом. Јавни подаци о временским тачкама трага из GPS API-ја који се сервирају путем API-ја за тачке праћења имаће референцу на вашу оригиналну страницу праћења. + „Следљиво“ значи да се траг не приказује нигде на јавним листама, али обрађене тачке праћења са временским ознакама у њима (које не могу бити директно повезане са вама) иду кроз преузимања са јавног GPS API-ја. Затвори ОСМ белешку Коментар ОСМ напомене Можете се пријавити користећи безбедан ОАут метод или користити своје корисничко име и лозинку. @@ -3776,12 +3776,12 @@ Претрага Кајак Моторни чамац - Додај у Мапилари - Додај у ОпенПлејсРевјуз - ОсмАнд приказује фотографије из неколико извора: -\nОпенПлејсРевјуз - ПОИ фотографије; -\nМапилари - слике на нивоу улице; -\nВеб / Викимедиа - ПОИ фотографије наведене у подацима ОпенСтритМап. + Додај у Mapillary + Додај у OpenPlaceReviews + OsmAnd приказује фотографије из неколико извора: +\nOpenPlaceReviews - фотографије тачака од интереса; +\nMapillary - слике на нивоу улице; +\nВеб / Викимедија - фотографије тачака од интереса наведене у OpenStreetMap подацима. Ресурси Приближна величина датотеке Изаберите податке за извоз у датотеку. @@ -3795,7 +3795,7 @@ ОсмАнд користи МГРС, који је сличан УТМ НАТО формату. Поједностављена стаза Само линија руте ће бити сачувана, а путне тачке ће бити избрисане. - Паузираће евидентирање стазе када се апликација убије (преко скорашњих програма). (Индикатор рада OsmAnd-а у позадини тада нестаје из обавештајне траке.) + Евидентирање стазе ће бити паузирано када се апликација убије (преко скорашњих програма). (Индикатор рада OsmAnd-а у позадини тада нестаје из обавештајне траке.) Наведите интервал евидентирања за опште снимање стаза (укључено помоћу виџета „Снимање путовања“ на мапи). Наставите снимање путовања Системско подразумевана @@ -3827,17 +3827,17 @@ Име: З - А Име: А - З Хвала вам што сте купили „Контурне линије“ - Претплата се наплаћује по изабраном периоду. Откажите га у АппГалери у било ком тренутку. - Уплата ће бити наплаћена са вашег рачуна АппГалери при потврди куповине. + Чланарина се наплаћује по изабраном периоду. Откажите је у AppGallery у било ком тренутку. + Уплата ће бити наплаћена са вашег рачуна AppGallery при потврди куповине. \n -\nПретплата се аутоматски обнавља уколико није отказана пре датума обнове. Ваш рачун ће бити задужен за период обнове (месец / три месеца / година) само на датум обнове. +\nЧланарина се аутоматски обнавља уколико није отказана пре датума обнове. Ваш рачун ће бити задужен за период обнове (месец / три месеца / година) само на датум обнове. \n -\nПретплатама можете управљати и отказати их тако што ћете отићи у подешавања апликације АппГалери. +\nЧланаринама можете управљати и отказати их тако што ћете отићи у подешавања апликације AppGallery. Избегавајте пешачке стазе Избегавајте пешачке стазе Развој - ОсмАнд лајв подаци - ОсмАнд лајв подаци + OsmAnd уживо подаци + OsmAnd уживо подаци Двофазно усмеравање за аутомобилску навигацију. Развој матичног јавног превоза Пребаците се на Јава (безбедан) прорачун рутирања јавног превоза @@ -3847,9 +3847,9 @@ Датотека је већ увезена у ОсмАнд Користите двофазни алгоритам усмеравања А* Графикон - %1$s подаци доступни само на путевима, морате израчунати руту користећи „Рута између тачака“ да бисте је добили. - Сачекајте поновно израчунавање руте. -\nГрафикон ће бити доступан након поновног израчунавања. + %1$s подаци доступни само на путевима, израчунајте руту користећи „Рута између тачака“ да бисте видели графике. + Молимо сачекајте. +\nГрафикон ће бити доступан након поновног израчунавања путање. Локалне мапе %1$s — %2$s Размак @@ -3871,15 +3871,15 @@ Користите корисничко име и лозинку Налог Пријавите се - Управљајте претплатом - Постоји проблем са вашом претплатом. Кликните на дугме да бисте отворили подешавања претплате за Гугле Плеј и да бисте поправили начин плаћања. - Претплата на ОсмАнд лајв је истекла - Претплата на ОсмАнд лајв је паузирана - Претплата на ОсмАнд лајв је на чекању + Управљајте чланарином + Постоји проблем са Вашом чланарином. Кликните на дугме да бисте отворили подешавања претплате за Гугле Плеј и да бисте поправили начин плаћања. + Чланарина за OsmAnd уживо је истекла + Чланарина на OsmAnd уживо је паузирана + Чланарина на OsmAnd уживо је на чекању Историја маркера Пошаљите ГПКС датотеку на ОпенСтритМап Унесите ознаке одвојене зарезом. - „Јавно“ значи да је траг јавно приказан у вашим ГПС траговима и на јавним ГПС траговима, као и на јавном списку трагова са временским ознакама у сировом облику. Подаци који се приказују путем АПИ-ја не упућују на вашу страницу трагова. Временске ознаке праћења нису доступне путем јавног ГПС АПИ-ја и тачке праћења нису хронолошки поређане. + „Јавно“ значи да је траг јавно приказан у вашим GPS траговима и на јавним GPS траговима, као и на јавном списку трагова са временским ознакама у сировом облику. Подаци који се приказују путем API-ја не упућују на вашу страницу трагова. Временске ознаке праћења нису доступне путем јавног GPS API-ја и тачке праћења нису хронолошки поређане. Није могуће отпремити слику, покушајте поново касније Изаберите слику Изаберите групе које ће бити увезене. @@ -3895,11 +3895,11 @@ Додајте нови сегмент • Додата је опција за извоз и увоз свих података, укључујући подешавања, ресурсе, моја места \n -\n • Планирање руте: графикони за сегменте са рутом, додата је могућност креирања и уређивања вишеструких сегмената стаза +\n • Планирање руте: графикони за сегменте са рутом, и додата је могућност креирања и уређивања вишеструких сегмената стаза \n \n • Додан је метод аутентификације ОАут за ОпенСтритМап, побољшан кориснички интерфејс ОСМ дијалога \n -\n • Прилагођене боје за омиљене и путне тачаке стаза +\n • Подршка за прилагођене боје за омиљене и пролазне тачке путања \n \n Вода @@ -3959,7 +3959,7 @@ Екетрични бициклизам Планински бициклизам Друмски бициклизам - Стаднардни бициклизам + Стандардни бициклизам Теретни камион Камионет Камион @@ -3988,4 +3988,98 @@ Преименуј путању Сачувај и настави Сви несачувани подаци ће бити изгубљени. + Преферира планинске пешачке путеве + Преферирај планинске пешачке путеве + Дозвољава водене токове који некад пресуше + Дозвољава потоке и одвоје + Дозволи потоке и одводе + Дозволи несталне водене токове + Кључ API-ја + Адреса сервера + Унесите параметар + Задржи је празном ако није + Адреса са свим параметрима ће изгледати овако: + Тестирај израчунавање пута + Фасцикле + Одаберите фасциклу + Одаберите фасциклу или додајте нову + Анализирај интервале поделе + Отпреми на OpenStreetMap + Измени фасциклу + сек + Пролажење + Прилазак + Припрема унапред + Припрема + Ван пута + Долазак на одредиште + Скретање + Интервали времена и удаљености + Времена објава различитих гласовних навођења зависе од типа објаве, као и тренутне и подразумеване брзине навођења. + Мрежни усмеривачи + Пешке + Вожња аутомобила + Измени мрежни усмеривач + Додај мрежни усмеривач + Мрежни усмеривач + Обриши овај мрежни усмеривач\? + Обриши пролазну тачку + Копирај у ознаке карте + Рељеф / Нагиб / Контурне линије + OpenPlaceReviews + Пријавите се за OpenPlaceReviews + Вожња ван пута + Прекидач да на карти покаже или сакрије справицу са координатама. + Удаљеност по кликтању + Последње OpenStreetMap ажурирање доступно: + Временски интервал бележења на који ће OsmAnd да пита за податке о тренутној позицији. + Прикажи почетни дијалог + Ако се искључи, снимање ће почети одмах после кликтања на справицу или на ставку менија, прескакајући дијалог потврде. + Прилагоди линију руте + Линија руте ће користити %1$s одабран на означеном стилу карте: %2$s. + Линија руте + Одаберите боју за режим карте: %1$s. + Немате ниједну куповину + Нови уређај / нови налог + Ако имате икаквих питања, контактирајте нас на %1$s. + Ако се Ваша куповина не појави овде, кликните на „%1$s” или контактирајте нашу подршку. + Контактирајте подршку + Решавање проблема + Пратите ову везу ако имате икаквих проблема са куповином. + OsmAnd уживо + Обнови чланарину + Годишња чланарина + Месечна чланарина + Тромесечна чланарина + Следећи датум наплате: %1$s + Отказано + У грејс периоду + На чекању + Истекло + Ажурирај све карте додате на %1$s\? + Број излаза + Објави када се премаши + Кориснички поени + Излаз + OpenPlaceReviews је пројакат покретан од стране заједнице о јавним местима, као што су ресторани, хотели, пролазне тачке. Пројекат сакупља јавне информације о њима, као што су слике, рецензије, везе ка другим местима као OpenStreetMap или Википедија. +\n +\nСви подаци на OpenPlaceReview су отворени и доступни свима: http://openplacereviews.org/data. +\n +\nМожете прочитати више на: http://openplacereviews.org + • Ажурирања OsmAnd уживо су премештена у „Преузимања > Ажурирања” +\n +\n • Путање се сада могу обојити по висини, брзини или нагибу. +\n +\n • Додата опција да се измени изглед линије која се приказује приликом навођења +\n +\n • Ажуриран дијалог за „Снимање пута” +\n +\n + Копирај адресу + Времена гласовних навођења + OsmAnd профил + Кориснички профил + Обрни све тачке + Одаберите профил које ће се користити по покретању апликације. + Последње коришћено \ No newline at end of file diff --git a/OsmAnd/res/values-sv/phrases.xml b/OsmAnd/res/values-sv/phrases.xml index bd0a59e189..4e9ec5b680 100644 --- a/OsmAnd/res/values-sv/phrases.xml +++ b/OsmAnd/res/values-sv/phrases.xml @@ -3926,4 +3926,6 @@ Ringar Diplomatiskt kontor Typ av vik + Platå + Social klubb \ No newline at end of file diff --git a/OsmAnd/res/values-sv/strings.xml b/OsmAnd/res/values-sv/strings.xml index 13c1dff07a..a64a0e4961 100644 --- a/OsmAnd/res/values-sv/strings.xml +++ b/OsmAnd/res/values-sv/strings.xml @@ -470,7 +470,7 @@ Indexerar kartan … Indexerar Intressepunkter … Indexerar transport … - Ett I/O-fel har inträffat + I/O-fel km km/h m @@ -736,7 +736,7 @@ Favoritpunkten {0} togs bort utan problem. Meddelande Författarnamn - Kommentaren lades till utan problem + Kommentaren tillagd Det gick inte att lägga till kommentar. Redigera POI Skapa POI @@ -759,7 +759,7 @@ OsmAnd-modul för höjdkurvor offline Avståndsmätning Ljudanteckningar - Denna modul för ljud- och videoanteckningar erbjuder möjlighet att skapa ljud-/foto-/videoanteckningar under en resa, antingen med hjälp av en knapp på kartskärmen eller direkt i kontextmenyn för en position på kartan. + Gör ljud-/foto-/videoanteckningar under en resa, med antingen en kartknapp eller platsens snabbmeny. Ljud-/Videoanteckningar delar Höjdkurvor @@ -1259,7 +1259,10 @@ Vi ändrade namnet på din favoritpunkt till %1$s för att underlätta din smiley i filnamnet. Välj hur mycket hastighetsgränsen måste överskridas för att du ska få ett röstmeddelande. Tolerans för hastighetsgräns - En anonym användare kan inte:\n- skapa grupper;\n- synkronisera grupper och enheter med servern;\n- hantera grupper och enheter på ett personligt ställe på webbplatsen. + Anonyma användare kan inte: +\n- Skapa grupper; +\n- Synkronisera grupper och enheter med servern; +\n- Hantera grupper och enheter i en personlig instrumentpanel på webbplatsen. Anonym användare Inloggad som %1$s Konfigurera kartan @@ -1357,11 +1360,11 @@ Anteckningar Online-karta Dela anteckning - Om du aktiverar denna vy ändras kartstilen till Nautisk och visar alla sjömärken och sjökortssymboler. + Detta plugin berikar OsmAnds kart- och navigationsapp för att också producera nautiska kartor för båtliv, segling och andra typer av vattensporter. \n -\nEn kartfil som innehåller alla nautiska symboler globalt finns tillgänglig som en enda nedladdning med namnet \'World seamarks\'. +\nEtt speciellt karttillägg för OsmAnd kommer att tillhandahålla alla nautiska navigationsmärken och kartsymboler, både för inland och för närliggande navigering. Beskrivningen av varje navigeringsmärke innehåller de detaljer som behövs för att identifiera dem och deras betydelse (kategori, form, färg, sekvens, referens etc.). \n -\nDenna vy kan ändras genom att antingen inaktivera den här igen eller genom att ändra kartstilen under Konfigurera kartor. +\nFör att återgå till en av OsmAnds konventionella kartstilar, helt enkelt avaktivera detta plugin igen, eller ändra \'Kartstil\' under \'Konfigurera karta\' efter önskemål. Detta plugin för OsmAnd ger dig detaljer om globala utförsåkning, längdskidspår, alpina skidvägar, linbanor och skidliftar. Rutter och pister visas färgkodade av svårigheter och avbildas i en speciell \"vinter\" -stil som liknar ett snöfärgat vinterlandskap. \n \nOm du aktiverar den här vyn ändras kartstilen till \"Vinter och skidor\" och visar alla landskapsfunktioner under vinterförhållanden. Denna vy kan återställas genom att antingen avaktivera den igen här, eller genom att ändra \'Kartstil\' under \'Konfigurera karta\' efter önskemål. @@ -1415,12 +1418,12 @@ Mer… Fler åtgärder Visa inte nästa gång - Kom ihåg mitt val + Kom ihåg valet Uppdatera Hämta Laddar ner… - Hämtningen lyckades - Ett oväntat fel uppstod + Nedladdat + Oväntat fel Åtgärd {0} Stäng Utgång @@ -1430,10 +1433,10 @@ Karta Favorit Favoriter - Lägg till i Favoriter + Lägg till i \'Favoriter\' Min position Mina platser - Mina favoriter + Favoriter Mina spår Spelar för tillfället in spår Ljud @@ -1459,21 +1462,21 @@ Dina redigeringar över tiden Ange väntetid att stanna kvar på ruttplaneringsskärmen. - Börja sväng-efter-sväng-navigering efter … + Starta sväng-för-sväng-vägledning efter … Kör - Du håller på att skicka %1$d ändring(ar) till OSM. Är du säker? - Vill du tömma historiken? + Är du säker på att du vill ladda upp %1$d ändringar till OSM\? + Rensa historik\? Aktuell rutt OSM-ändringar lades till i lokala ändringar Markera för att ta bort - I många länder (Tyskland, Frankrike, Italien och andra) är det inte tillåtet att använda varningar för hastighetskameror. OsmAnd tar inget ansvar om du bryter mot lagen. Tryck på Ja endast om du får använda denna funktion. + I många länder (Tyskland, Frankrike, Italien och andra) är användning av hastighetskameravarningar olaglig. OsmAnd tar inget ansvar om du bryter mot lagen. Klicka bara på \'Ja\' om du är berättigad att använda den här funktionen. Hämta kartor För att avspegla dina trafiksignaler och -förordningar på ett korrekt sätt, välj den region du kör i: OsmAnd erbjuder global frånkopplad kartsurfning och frånkopplad navigering. Välkommen A-GPS-info Meddelande - A-GPS-data senast nedladdade: %1$s + A-GPS-data har laddats ner: %1$s Använd inte Adress Visa beskrivning. @@ -1488,9 +1491,9 @@ Europa - Nederländerna Högkontrastvägar Standard - Kopiera OsmAnds datafiler till den nya destinationen? + Flytta OsmAnds datafiler till den nya destinationen\? Kunde inte skapa kartor i den angivna mappen - Kopieringen misslyckades + Det gick inte att flytta filer Extern lagring Lagring för flera användare Internt appminne @@ -1512,16 +1515,16 @@ Wikipedia Wikipedia Visa detaljer - Du håller på att ta bort %1$d anteckningar. Är du säker? + Är du säker på att du vill ta bort %1$d anteckningar\? Wikipedia Importera till OsmAnd Platstjänsten är av. Vill du slå på den\? Du har gamla och inkompatibla data från Wikipedia. Vill du arkivera dem? - Hämta ytterligare data från Wikipedia (%1$s MB)? + Hämta ytterligare data från Wikipedia (%1$s MB)\? Volapük Thai Telugu - Norska (nynorska) + Norska (Nynorska) Newar/Nepal Bhasa Malajiska Haitiska @@ -1565,7 +1568,7 @@ Genomskinlig blå Purpur Genomskinlig purpur - Starta om appen manuellt för att ändringarna ska börja gälla. + En omstart krävs för att tillämpa ändringen. GPX-färg Gul Standard (13) @@ -1589,8 +1592,8 @@ ÅNGRA Hoppa över OsmAnd - Undvik pendeltåg - Undvik att använda pendeltåg + Inget pendeltåg + Undviker att använda pendeltåg Insticksprogram Varning Grundläggande @@ -1600,7 +1603,7 @@ Öppnar Stänger Platser - GPX-fil med anteckningar om platser. + GPX-fil med platser. Typ av POI Lägg till öppettider Kontaktinformation @@ -1620,7 +1623,7 @@ Sluta simulera din plats. Simulera din plats med en beräknad rutt eller med ett inspelat GPX-spår. %1$s nedladdningar kvar - Information om favoriter + Favoritinformation Vägar Lägg till favorit Ange enhet för hastighet. @@ -1634,7 +1637,7 @@ Sjömil per timme (knop) Inspelning av resa Navigering - Körs i bakgrunden + Kör i bakgrunden Lägg till ny Välj kategori Antal rader @@ -1653,7 +1656,7 @@ Aktivera modulen SRTM Senare Fullversionen - Det angivna kategorinamnet finns redan. Ange ett annat namn. + Använd ett kategorinamn som inte redan finns. Kategorinamn Lägg till en ny kategori %.1f MB @@ -1668,9 +1671,9 @@ Dela platsen Sänd Skuggad relief-lager inaktiverat - \'Av\' startar kartskärmen direkt. + \'Av\' startar kartan direkt. Karta hämtad - Kartan över %1$s har hämtats, och du kan nu börja använda den. + %1$s -kartan är redo att användas. Visa kartan QR-kod Ange land @@ -1693,7 +1696,7 @@ Versioner Kontakta oss Skapat en OSM POI - Baskarta över världen (täcker hela världen med låg zoomningsgrad) saknas eller är gammal. Hämta gärna denna karta för en global översikt. + Världskarta (täcker hela världen vid låg zoomnivå) saknas eller är föråldrad. Överväg att ladda ner den för en global översikt. Skicka Teckenförklaring Uppdatering @@ -1721,9 +1724,9 @@ Lägg till kommentar Öppna anteckning igen Stäng anteckning - En anteckning har skapats utan problem + Anteckningen har skapats Det gick inte att skapa anteckningen. - Anteckningen stängdes utan problem + Anteckningen stängd Det gick inte att stänga anteckningen. Ta bort GPX-waypoint? Redigera GPX-waypoint @@ -1767,8 +1770,8 @@ Tid: Appen har inte tillåtelse att använda SD-kortet Ge platsåtkomst. - Appen har inte tillräckliga behörigheter för att komma åt kameran. - Appen har inte tillräckliga behörigheter för att komma åt mikrofonen. + Ge kameraåtkomst. + Ge mikrofonåtkomst. Välj röstvägledning Välj eller hämta röstvägledning för ditt språk. Välj de vägar du vill undvika under navigering. @@ -1790,7 +1793,7 @@ Startpunkt Post borttagen poster raderade - ÅNGRA ALLA + Ångra allt POI-ikoner Skifta startpunkt och destination Antal bidragsgivare @@ -1798,10 +1801,10 @@ Rapport för Välj Ta bort - Du kan ta bort hämtade uppdateringar och få tillbaka originalkartan + Ta bort hämtade uppdateringar och få tillbaka originalkartan Lägg till tidsspann Blockerad väg - Inga data tillgängliga + Ingen data tillgänglig Underjordiska objekt Läs mer Lagringsutrymme @@ -1810,18 +1813,18 @@ Spara ändringar Hitta en parkeringsplats Visa polygoner - Visa MTB-rutter + Visa mountainbike spår Välj kartmarkörer Omvänd ordning Aktivera kartmarkörerna. - Vill du ta bort alla aktiva markörer? - Vill du radera kartmarkörshistoriken? + Ta bort alla aktiva markörer\? + Radera kartmarkörshistoriken\? Aktiva markörer Kartmarkörer Kartmarkör - Vill du lägga till alla punkter i Kartmarkörer? + Lägg till alla punkter som kartmarkörer\? Lägg till i Kartmarkörer - Rekommendationen är att slå av rendering av polygoner. + Det är rekommenderat att slå av rendering av polygoner. Första kartmarkör Andra kartmarkör Verktygsfält @@ -1839,34 +1842,34 @@ Donationer Antal mottagare Redigeringar %1$s, rang %2$s, redigeringar totalt %3$s - Ranglista OSM-redigerare + OSM Editor rankning OsmAnd Live-prenumeration Prenumerera - Krävs för att ge er information om bidrag. - Publikt namn + Behövs för att uppdatera dig om dina bidrag. + Offentligt namn Visa inte mitt namn i rapporter - Månadskostnad + Kostnad per månad Månadsbetalning Aktiv Inaktiv Ange en giltig e-postadress - Ange publikt namn + Ange ett offentligt namn Tack för att du stödjer OsmAnd! \nFör att aktivera alla nya funktioner behöver du starta om OsmAnd. Delar av din donation kommer att skickas till OSM-användare som skickar in kartändringar i det området. Prenumerationsinställningar - Köp en OsmAnd Live-prenumeration först + Köp först ett abonnemang på OsmAnd Live Visa transparent sökfält Beräkna om rutten Toppfält Delat minne Undvik väg - Den för tillfället valda datalagringsmappen är skrivskyddad. Lagringsmappen har tillfälligt ändrats till internminnet. Välj en giltig datalagringsmapp. - Flytta upp - Flytta ned + Växlade till internminnet eftersom den valda datalagringsmappen är skrivskyddad. Välj en skrivbar lagringskatalog. + Flytta ↑ + Flytta ↓ Avsluta navigeringen Fullständig rapport - OpenStreetMap inloggning och lösenord + OSM-användarnamn och lösenord Lägg till kartmarkörer via kartan Hittar inga waypoints Relativ bäring @@ -1897,12 +1900,12 @@ Mellan Tjock Rapporter - Nu har appen tillåtelse att skriva till extern lagringsplats. En manuell omstart av appen krävs. + Appen får nu skriva till extern lagring, men måste startas igen för att göra det. Stödregion Denna prenumeration aktiverar uppdateringar varje timme av kartor runt omkring i världen. \nEn del av inkomsterna går tillbaka till OSM-gemenskapen och betalas ut för varje OSM-bidrag. \nOm du tycker om OsmAnd och OSM och vill stödja och stödjas av dem så är detta ett utmärkt sätt att göra det på. - Destinationen är inte fastställd + Tillgänglighetsplugin: Ingen destination inställd Magnetisk bäring Klipplängd Filnamnet innehåller ogiltiga tecken @@ -1968,7 +1971,7 @@ Krävs för att hämta kartor. Söker efter plats… Oanvänt utrymme - OsmAnds lagringsplats för data (för kartor, GPX-filer med mera): %1$s. + OsmAnds lagringsplats för data (för kartor, spårfiler med mera): %1$s. Bevilja tillstånd Tillåt platsåtkomst Uppdatera alla kartor nu\? @@ -1978,7 +1981,7 @@ Är du säker på att du vill ersätta favoriten %1$s? Rensa alla rutor Ange stad, adress och POI-namn - Donation till OpenStreetMap-gemenskapen + Donation till OSM-communityn Skaffa det Skaffa för %1$s Du har inte några offline-kartor installerade. Du kan välja en karta från listan eller hämta kartor senare via \'Meny - %1$s\'. @@ -1993,7 +1996,7 @@ Minsta loggningsprecision Filter: Ingen loggning om inte denna noggrannhet uppnås. Jul-POI - Visa Jul-POI? + Visa POI:er för julhelgen\? Ljusbrun Mörkbrun Färgschema för konturer @@ -2004,26 +2007,26 @@ Inspelat Spela in Inga data - Visa en systemavisering som gör att du kan starta trippinspelning. - Tillåt motorvägar - Tillåt motorvägar. + Visa ett systemmeddelande som gör det möjligt att starta trippinspelning. + Använd motorvägar + Tillåter motorvägar. Närliggande Wikipedia-artiklar - Sök stad eller region + Stad eller region Tag den %1$d avfarten och kör Ladda upp POI Ruttberäkning - Du har inga GPX-filer ännu - Du kan också lägga till GPX-filer i mappen + Du har inga spårfiler än + Du kan också lägga till spårfiler i mappen Lägg till mer… Utseende - Aktivera snabbstart av inspelning - Med tanke på den kommande julen och nyåret så kan du välja att visa POI som har med julen att göra: julgranar, julmarknader med mera. + Aktivera snabbinspelning + Förutse jul- och nyårsferier kan du välja att visa tillhörande intressepunkter som julgranar och marknader etc. Bredd på konturlinjen Vatten - Prenumerationsavgiften faktureras varje månad. Avbryt den när som helst i Google Play. - En del av din donation skickas till OSM-användare som skickar in ändringar till OpenStreetMap. Kostnaden för abonnemanget förblir densamma. + Prenumerationen debiteras per vald period. Avbryt det på Google Play när som helst. + En del av din donation skickas till OSM-bidragsgivare. Prenumerationskostnaden förblir densamma. En prenumeration aktiverar uppdateringar varje timme, dag eller vecka och obegränsade nedladdningar av alla kartor globalt. - Erhåll obegränsade karthämtningar och kartuppdateringar oftare än en gång i månaden: varje vecka, dagligen eller varje timme. + Få obegränsad nedladdning av kartor, lägg till uppdateringar varje vecka, dagligen eller till och med varje timme. Låg Bredd på konturlinjen Hög @@ -2069,7 +2072,7 @@ \n Kort OLC \nVänligen tillhandahåll fullständig kod - Filen kan inte flyttas. + Det gick inte att flytta filen. Flytta Spår Körstil @@ -2104,7 +2107,7 @@ Tryck på åtgärdsknappen sätter en kartmarkör i centrum av skärmen. Lägg till ny mapp Punkt(er) togs bort. - Du håller på att ta bort %1$d punkt(er). Är du säker? + Är du säker på att du vill ta bort %1$d punkt(er)\? Kurvor att passera längs rutten Vägpunkter, sevärdheter, namngivna funktioner Spår @@ -2135,11 +2138,11 @@ Välj gator i %1$s Ange adress - Ange stad + Skriv stad/samhälle/ort Ange postnummer Närmaste städer Välj stad - Välj postnummer + Postnummer sökning Autozoomning på/av Knapp för att visa eller dölja OSM-anteckningar på kartan. Starta om sökningen @@ -2153,7 +2156,7 @@ Gruppnamn Ändra färg Redigera namn - Lägg till destination + Ange destination Ersätt destination Inget överlägg Inget underlägg @@ -2212,7 +2215,7 @@ Datum Ange användarnamn Användarnamn - Animera min plats + Animera din egen position Analysera på kartan Återställ köp Pausad @@ -2396,15 +2399,15 @@ Tillåt privat åtkomst Tillåta åtkomst till privata områden. Visa zoom-nivå: %1$s - Aktivera kartpanoreringsanimation av \'Min Position\' under navigering. + Aktivera animerad kartpanning av \"Min position\" under navigering. Översikt - Ett tryck på den här åtgärdsknappen kommer att slå på/av automatisk zoomkarta enligt din hastighet. - Aktivera autozoom karta - Inaktivera autozoom karta + Knapp för att slå på eller av hastighetsstyrd automatisk zoom. + Aktivera autozoom + Inaktivera autozoom Lägg till första mellanliggande - Genom att trycka på den här åtgärdsknappen blir skärmens mittpunkt resmålet, alla tidigare valda mål blir den senaste mellanliggande destinationen. - Genom att trycka på den här åtgärdsknappen kan skärmen centrera den nya ruttdestinationen och ersätta den tidigare valda destinationen (om någon). - Genom att trycka på denna åtgärdsknapp kommer skärmens mittpunkt att bli den första mellanliggande destinationen. + En knapp för att göra skärmen centrerad till ruttdestinationen, en tidigare vald destination skulle bli den sista mellandestinationen. + En knapp för att göra skärmen centrerad till den nya ruttdestinationen och ersätta den tidigare valda destinationen (om någon). + En knapp för att göra skärmens centrum till den första mellanliggande destinationen. Anmäl dig till vår e-postlista om apprabatter och få 3 extra kartnedladdningar! Havsdjupskonturer och sjömärken. Tack för att du köpt \"Nautiska djupkonturer\" @@ -3358,4 +3361,50 @@ Förnya prenumerationen I nådeperioden Pausad + Nattkarta + Lägg till onlinekälla + Om du använder dessa ändringar raderas cachade data för denna kakelkälla + Ställ in fartygets höjd + Du kan ställa in fartygets höjd för att undvika låga broar. Tänk på att om bron är rörlig kommer vi att använda dess höjd i öppet tillstånd. + Ställ in fartygets höjd för att undvika låga broar. Tänk på att om bron är rörlig kommer vi att använda dess höjd i öppet tillstånd. + Ställ in fartygets bredd för att undvika smala broar + En växling för att visa eller dölja Mapillary-lagret på kartan. + Ange tillåten fordonslängd på rutter. + Längdsgräns + Bäring + %1$s har tagits bort + Starta om appen för att radera all hastighetskameradata. + Avinstallera och starta om + Den här enheten har inte hastighetskameror. + Rullskridskor + Ta bort närmaste destination + Kontrollera zoomningsnivån för kartan med volymknapparna på enheten. + Volymknappar som zoom + Ange ett namn för punkten + Raderar nästa destination på din rutt. Om det är den slutliga destinationen kommer navigationen att stoppas. + Den tillagda punkten kommer inte att synas på kartan, eftersom den valda gruppen är dold kan du hitta den i \"%s\". + Enduro motorcykel + Skoter + Rullstol + Rullstol framåt + Visa startdialogrutan + Om den är inaktiverad startar inspelningen direkt efter att du har tryckt på widgeten eller menyalternativet och hoppat över bekräftelsedialogrutan. + Anpassa ruttlinjen + Ruttlinje + Ruttlinje skulle användas %1$s som anges i vald kartstil: %2$s. + Upphörd + • OsmAnd Live-uppdateringar flyttade till \"Nedladdningar> Uppdateringar\" +\n +\n• Spår kan nu färga efter höjd, hastighet eller lutning. +\n +\n• Lagt till alternativ för att ändra utseendet på navigeringslinjen +\n +\n• Uppdaterad \"Trip recording\" -dialog +\n +\n + Uppdatera alla kartor som lagts till i %1$s\? + Utgångsnummer + Meddela när den överskrids + Användarpunkter + Utgång \ No newline at end of file diff --git a/OsmAnd/res/values-tr/strings.xml b/OsmAnd/res/values-tr/strings.xml index ca24c81192..473ad3ed09 100644 --- a/OsmAnd/res/values-tr/strings.xml +++ b/OsmAnd/res/values-tr/strings.xml @@ -4072,7 +4072,11 @@ Ek süre içinde Beklemede Süresi doldu - • OsmAnd Live güncellemeleri \"İndirmeler> Güncellemeler\" bölümüne taşındı + • Eş yükselti eğrilerini fit cinsinden indirme seçeneği eklendi +\n +\n • Güzergah Planla görünümü: noktalar veya grafikler arasında geçiş yapmak için sekmeler eklendi +\n +\n • OsmAnd Live güncellemeleri \"İndirmeler > Güncellemeler\" bölümüne taşındı \n \n • Yollar artık rakım, hız veya eğime göre renklendirilebilir \n @@ -4087,4 +4091,8 @@ Kullanıcı puanları Çıkış %1$s → … + fit + Eş yükselti eğrileri birimi biçimi + OsmAnd, metre ve fit cinsinden eş yükselti eğrileri verileri sağlar. Biçimi değiştirmek için dosyayı yeniden indirmeniz gerekecek. + Lütfen gerekli biçimi seçin. Biçimi değiştirmek için dosyayı yeniden indirmeniz gerekecek. \ No newline at end of file diff --git a/OsmAnd/res/values-uk/phrases.xml b/OsmAnd/res/values-uk/phrases.xml index 9c97460fd4..4ddbb1778a 100644 --- a/OsmAnd/res/values-uk/phrases.xml +++ b/OsmAnd/res/values-uk/phrases.xml @@ -3926,4 +3926,6 @@ Карате Дипломатичне відомство Тип затоки + Плато + Суспільний клуб \ No newline at end of file diff --git a/OsmAnd/res/values-uk/strings.xml b/OsmAnd/res/values-uk/strings.xml index 4fe37da2e3..cb45c0658b 100644 --- a/OsmAnd/res/values-uk/strings.xml +++ b/OsmAnd/res/values-uk/strings.xml @@ -4053,7 +4053,11 @@ Лінія маршруту Лінія маршруту застосовуватиме %1$s, вказаний у вибраному стилі мапи: %2$s. Вкажіть колір для режиму мапи: %1$s. - • Оновлення OsmAnd Live переміщено до «Завантаження >Оновлення» + • Додана можливість завантаження контурних ліній у футах +\n +\n• Ландшафтне планування маршруту: додані вкладки для перемикання між точками або графіками +\n +\n• Оновлення OsmAnd Live переміщено до «Завантаження >Оновлення» \n \n• Тепер треки можуть бути забарвлені за висотою, швидкістю або нахилом. \n @@ -4084,4 +4088,8 @@ Повідомляти про перевищення Користувацькі точки Вивід + 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 09f71464d7..caff9ec09b 100644 --- a/OsmAnd/res/values-zh-rTW/phrases.xml +++ b/OsmAnd/res/values-zh-rTW/phrases.xml @@ -3926,4 +3926,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 358a89a8ec..1db440ac46 100644 --- a/OsmAnd/res/values-zh-rTW/strings.xml +++ b/OsmAnd/res/values-zh-rTW/strings.xml @@ -4047,7 +4047,11 @@ 路線 路線將會使用 %1$s 在選定的地圖樣式上指定的:%2$s。 指定地圖模式的顏色:%1$s。 - • OsmAnd Live 更新移動至「下載 > 更新」 + • 新增以英呎為單位下載等高線 +\n +\n • 規劃路線樣式:新增切換點與圖形的分頁 +\n +\n • OsmAnd Live 更新移動至「下載 > 更新」 \n \n • 軌跡現在可以使用海拔、速度或坡度來填色 \n @@ -4079,4 +4083,8 @@ 使用者點 輸出 %1$s → … + 英呎 + 等高線單位格式 + OsmAnd 提供以公尺與英呎為單位的等高線資料。您將必須重新下載檔案以變更格式。 + 請選取需要格式。您將必須重新下載檔案以變更格式。 \ No newline at end of file diff --git a/OsmAnd/res/values/phrases.xml b/OsmAnd/res/values/phrases.xml index 2089642add..ba5a5dfe4b 100644 --- a/OsmAnd/res/values/phrases.xml +++ b/OsmAnd/res/values/phrases.xml @@ -4381,4 +4381,8 @@ Diplomatic office + Plateau + + Social club + diff --git a/OsmAnd/res/values/strings.xml b/OsmAnd/res/values/strings.xml index 0a6f64787d..28204402e8 100644 --- a/OsmAnd/res/values/strings.xml +++ b/OsmAnd/res/values/strings.xml @@ -19,8 +19,14 @@ User points Announce when exceeded Exit number + Please select the needed format. You will need to re-download the file to change the format. + OsmAnd provides contour lines data in meters and feet. You will need to re-download the file to change the format. + Contour lines unit format + feet Update all maps added to %1$s? + • Added option to download Contour lines in feet\n\n + • Plan Route landscape: added tabs to switch between points or graphs\n\n • OsmAnd Live updates moved to \"Downloads > Updates\"\n\n • Tracks now could be colorizing by altitude, speed, or slope.\n\n • Added option to change the appearance of the navigation route line\n\n diff --git a/OsmAnd/src-google/net/osmand/plus/inapp/InAppPurchaseHelperImpl.java b/OsmAnd/src-google/net/osmand/plus/inapp/InAppPurchaseHelperImpl.java index df05570f57..32597863d1 100644 --- a/OsmAnd/src-google/net/osmand/plus/inapp/InAppPurchaseHelperImpl.java +++ b/OsmAnd/src-google/net/osmand/plus/inapp/InAppPurchaseHelperImpl.java @@ -18,11 +18,12 @@ import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandPlugin; import net.osmand.plus.R; import net.osmand.plus.inapp.InAppPurchases.InAppPurchase; +import net.osmand.plus.inapp.InAppPurchases.InAppPurchase.PurchaseState; import net.osmand.plus.inapp.InAppPurchases.InAppSubscription; import net.osmand.plus.inapp.InAppPurchases.InAppSubscription.SubscriptionState; +import net.osmand.plus.inapp.InAppPurchases.PurchaseInfo; import net.osmand.plus.inapp.InAppPurchasesImpl.InAppPurchaseLiveUpdatesOldSubscription; import net.osmand.plus.inapp.util.BillingManager; -import net.osmand.plus.settings.backend.CommonPreference; import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.srtmplugin.SRTMPlugin; import net.osmand.util.Algorithms; @@ -174,7 +175,8 @@ public class InAppPurchaseHelperImpl extends InAppPurchaseHelper { }); } for (Purchase purchase : purchases) { - if (!purchase.isAcknowledged()) { + InAppSubscription subscription = getLiveUpdates().getSubscriptionBySku(purchase.getSku()); + if (!purchase.isAcknowledged() || (subscription != null && !subscription.isPurchased())) { onPurchaseFinished(purchase); } } @@ -310,8 +312,8 @@ public class InAppPurchaseHelperImpl extends InAppPurchaseHelper { } } } - for (Entry entry : subscriptionStateMap.entrySet()) { - SubscriptionState state = entry.getValue(); + for (Entry entry : subscriptionStateMap.entrySet()) { + SubscriptionState state = entry.getValue().state; if (state == SubscriptionState.PAUSED || state == SubscriptionState.ON_HOLD) { String sku = entry.getKey(); if (!result.contains(sku)) { @@ -492,15 +494,17 @@ public class InAppPurchaseHelperImpl extends InAppPurchaseHelper { } private PurchaseInfo getPurchaseInfo(Purchase purchase) { - return new PurchaseInfo(purchase.getSku(), purchase.getOrderId(), purchase.getPurchaseToken()); + return new PurchaseInfo(purchase.getSku(), purchase.getOrderId(), purchase.getPurchaseToken(), + purchase.getPurchaseTime(), purchase.getPurchaseState(), purchase.isAcknowledged(), purchase.isAutoRenewing()); } private void fetchInAppPurchase(@NonNull InAppPurchase inAppPurchase, @NonNull SkuDetails skuDetails, @Nullable Purchase purchase) { if (purchase != null) { - inAppPurchase.setPurchaseState(InAppPurchase.PurchaseState.PURCHASED); - inAppPurchase.setPurchaseTime(purchase.getPurchaseTime()); + inAppPurchase.setPurchaseState(PurchaseState.PURCHASED); + inAppPurchase.setPurchaseInfo(ctx, getPurchaseInfo(purchase)); } else { - inAppPurchase.setPurchaseState(InAppPurchase.PurchaseState.NOT_PURCHASED); + inAppPurchase.setPurchaseState(PurchaseState.NOT_PURCHASED); + inAppPurchase.restorePurchaseInfo(ctx); } inAppPurchase.setPrice(skuDetails.getPrice()); inAppPurchase.setPriceCurrencyCode(skuDetails.getPriceCurrencyCode()); @@ -519,18 +523,17 @@ public class InAppPurchaseHelperImpl extends InAppPurchaseHelper { } if (inAppPurchase instanceof InAppSubscription) { InAppSubscription s = (InAppSubscription) inAppPurchase; - - SubscriptionState state = subscriptionStateMap.get(inAppPurchase.getSku()); - s.setState(state == null ? SubscriptionState.UNDEFINED : state); - CommonPreference statePref = ctx.getSettings().registerStringPreference( - s.getSku() + "_state", SubscriptionState.UNDEFINED.getStateStr()).makeGlobal(); - s.setPrevState(SubscriptionState.getByStateStr(statePref.get())); - statePref.set(s.getState().getStateStr()); + s.restoreState(ctx); + s.restoreExpireTime(ctx); + SubscriptionStateHolder stateHolder = subscriptionStateMap.get(s.getSku()); + if (stateHolder != null) { + s.setState(ctx, stateHolder.state); + s.setExpireTime(ctx, stateHolder.expireTime); + } if (s.getState().isGone() && s.hasStateChanged()) { ctx.getSettings().LIVE_UPDATES_EXPIRED_FIRST_DLG_SHOWN_TIME.set(0L); ctx.getSettings().LIVE_UPDATES_EXPIRED_SECOND_DLG_SHOWN_TIME.set(0L); } - String introductoryPrice = skuDetails.getIntroductoryPrice(); String introductoryPricePeriod = skuDetails.getIntroductoryPricePeriod(); int introductoryPriceCycles = skuDetails.getIntroductoryPriceCycles(); diff --git a/OsmAnd/src-huawei/net/osmand/plus/inapp/InAppPurchaseHelperImpl.java b/OsmAnd/src-huawei/net/osmand/plus/inapp/InAppPurchaseHelperImpl.java index 6f930fec1d..704c086f47 100644 --- a/OsmAnd/src-huawei/net/osmand/plus/inapp/InAppPurchaseHelperImpl.java +++ b/OsmAnd/src-huawei/net/osmand/plus/inapp/InAppPurchaseHelperImpl.java @@ -29,6 +29,7 @@ import net.osmand.plus.OsmandApplication; import net.osmand.plus.inapp.InAppPurchases.InAppPurchase; import net.osmand.plus.inapp.InAppPurchases.InAppSubscription; import net.osmand.plus.inapp.InAppPurchases.InAppSubscriptionIntroductoryInfo; +import net.osmand.plus.inapp.InAppPurchases.PurchaseInfo; import net.osmand.plus.inapp.InAppPurchasesImpl.InAppPurchaseLiveUpdatesOldSubscription; import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.util.Algorithms; @@ -48,7 +49,7 @@ public class InAppPurchaseHelperImpl extends InAppPurchaseHelper { private List productInfos; private OwnedPurchasesResult ownedSubscriptions; - private List ownedInApps = new ArrayList<>(); + private final List ownedInApps = new ArrayList<>(); public InAppPurchaseHelperImpl(OsmandApplication ctx) { super(ctx); @@ -233,15 +234,18 @@ public class InAppPurchaseHelperImpl extends InAppPurchaseHelper { } private PurchaseInfo getPurchaseInfo(InAppPurchaseData purchase) { - return new PurchaseInfo(purchase.getProductId(), purchase.getSubscriptionId(), purchase.getPurchaseToken()); + return new PurchaseInfo(purchase.getProductId(), purchase.getSubscriptionId(), purchase.getPurchaseToken(), + purchase.getPurchaseTime(), purchase.getPurchaseState(), true, purchase.isAutoRenewing()); } private void fetchInAppPurchase(@NonNull InAppPurchase inAppPurchase, @NonNull ProductInfo productInfo, @Nullable InAppPurchaseData purchaseData) { if (purchaseData != null) { inAppPurchase.setPurchaseState(InAppPurchase.PurchaseState.PURCHASED); inAppPurchase.setPurchaseTime(purchaseData.getPurchaseTime()); + inAppPurchase.setPurchaseInfo(ctx, getPurchaseInfo(purchaseData)); } else { inAppPurchase.setPurchaseState(InAppPurchase.PurchaseState.NOT_PURCHASED); + inAppPurchase.restorePurchaseInfo(ctx); } inAppPurchase.setPrice(productInfo.getPrice()); inAppPurchase.setPriceCurrencyCode(productInfo.getCurrency()); diff --git a/OsmAnd/src/net/osmand/AndroidNetworkUtils.java b/OsmAnd/src/net/osmand/AndroidNetworkUtils.java index baf7790a87..b25fb1b2d4 100644 --- a/OsmAnd/src/net/osmand/AndroidNetworkUtils.java +++ b/OsmAnd/src/net/osmand/AndroidNetworkUtils.java @@ -3,10 +3,10 @@ package net.osmand; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.AsyncTask; -import android.util.Pair; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.annotation.WorkerThread; import net.osmand.osm.io.NetworkUtils; import net.osmand.plus.OsmandApplication; @@ -38,6 +38,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; public class AndroidNetworkUtils { @@ -56,6 +57,15 @@ public class AndroidNetworkUtils { void onFilesUploadDone(@NonNull Map errors); } + public interface OnFilesDownloadCallback { + @Nullable + Map getAdditionalParams(@NonNull File file); + void onFileDownloadProgress(@NonNull File file, int percent); + @WorkerThread + void onFileDownloadedAsync(@NonNull File file); + void onFilesDownloadDone(@NonNull Map errors); + } + public static class RequestResponse { private Request request; private String response; @@ -74,35 +84,46 @@ public class AndroidNetworkUtils { } } - public interface OnRequestsResultListener { - void onResult(@NonNull List results); + public interface OnSendRequestsListener { + void onRequestSent(@NonNull RequestResponse response); + void onRequestsSent(@NonNull List results); } - public static void sendRequestsAsync(final OsmandApplication ctx, - final List requests, - final OnRequestsResultListener listener) { + public static void sendRequestsAsync(@Nullable final OsmandApplication ctx, + @NonNull final List requests, + @Nullable final OnSendRequestsListener listener) { - new AsyncTask>() { + new AsyncTask>() { @Override protected List doInBackground(Void... params) { List responses = new ArrayList<>(); for (Request request : requests) { + RequestResponse requestResponse; try { String response = sendRequest(ctx, request.getUrl(), request.getParameters(), request.getUserOperation(), request.isToastAllowed(), request.isPost()); - responses.add(new RequestResponse(request, response)); + requestResponse = new RequestResponse(request, response); } catch (Exception e) { - responses.add(new RequestResponse(request, null)); + requestResponse = new RequestResponse(request, null); } + responses.add(requestResponse); + publishProgress(requestResponse); } return responses; } + @Override + protected void onProgressUpdate(RequestResponse... values) { + if (listener != null) { + listener.onRequestSent(values[0]); + } + } + @Override protected void onPostExecute(@NonNull List results) { if (listener != null) { - listener.onResult(results); + listener.onRequestsSent(results); } } @@ -146,7 +167,7 @@ public class AndroidNetworkUtils { @Override protected String doInBackground(Void... params) { - return downloadFile(url, fileToSave); + return downloadFile(url, fileToSave, false, null); } @Override @@ -158,8 +179,80 @@ public class AndroidNetworkUtils { }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null); } - public static String sendRequest(OsmandApplication ctx, String url, Map parameters, - String userOperation, boolean toastAllowed, boolean post) { + public static void downloadFilesAsync(final @NonNull String url, + final @NonNull List files, + final @NonNull Map parameters, + final @Nullable OnFilesDownloadCallback callback) { + + new AsyncTask>() { + + @Override + @NonNull + protected Map doInBackground(Void... v) { + Map errors = new HashMap<>(); + for (final File file : files) { + final int[] progressValue = {0}; + publishProgress(file, 0); + IProgress progress = null; + if (callback != null) { + progress = new NetworkProgress() { + @Override + public void progress(int deltaWork) { + progressValue[0] += deltaWork; + publishProgress(file, progressValue[0]); + } + }; + } + try { + Map params = new HashMap<>(parameters); + if (callback != null) { + Map additionalParams = callback.getAdditionalParams(file); + if (additionalParams != null) { + params.putAll(additionalParams); + } + } + boolean firstPrm = !url.contains("?"); + StringBuilder sb = new StringBuilder(url); + for (Entry entry : params.entrySet()) { + sb.append(firstPrm ? "?" : "&").append(entry.getKey()).append("=").append(URLEncoder.encode(entry.getValue(), "UTF-8")); + firstPrm = false; + } + String res = downloadFile(sb.toString(), file, true, progress); + if (res != null) { + errors.put(file, res); + } else { + if (callback != null) { + callback.onFileDownloadedAsync(file); + } + } + } catch (Exception e) { + errors.put(file, e.getMessage()); + } + publishProgress(file, Integer.MAX_VALUE); + } + return errors; + } + + @Override + protected void onProgressUpdate(Object... objects) { + if (callback != null) { + callback.onFileDownloadProgress((File) objects[0], (Integer) objects[1]); + } + } + + @Override + protected void onPostExecute(@NonNull Map errors) { + if (callback != null) { + callback.onFilesDownloadDone(errors); + } + } + + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null); + } + + public static String sendRequest(@Nullable OsmandApplication ctx, @NonNull String url, + @Nullable Map parameters, + @Nullable String userOperation, boolean toastAllowed, boolean post) { HttpURLConnection connection = null; try { @@ -177,7 +270,7 @@ public class AndroidNetworkUtils { String paramsSeparator = url.indexOf('?') == -1 ? "?" : "&"; connection = NetworkUtils.getHttpURLConnection(params == null || post ? url : url + paramsSeparator + params); connection.setRequestProperty("Accept-Charset", "UTF-8"); - connection.setRequestProperty("User-Agent", Version.getFullVersion(ctx)); + connection.setRequestProperty("User-Agent", ctx != null ? Version.getFullVersion(ctx) : "OsmAnd"); connection.setConnectTimeout(15000); if (params != null && post) { connection.setDoInput(true); @@ -200,9 +293,10 @@ public class AndroidNetworkUtils { } if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) { - if (toastAllowed) { - String msg = userOperation - + " " + ctx.getString(R.string.failed_op) + ": " + connection.getResponseMessage(); + if (toastAllowed && ctx != null) { + String msg = (!Algorithms.isEmpty(userOperation) ? userOperation + " " : "") + + ctx.getString(R.string.failed_op) + ": " + + connection.getResponseMessage(); showToast(ctx, msg); } } else { @@ -233,17 +327,17 @@ public class AndroidNetworkUtils { } catch (NullPointerException e) { // that's tricky case why NPE is thrown to fix that problem httpClient could be used - if (toastAllowed) { + if (toastAllowed && ctx != null) { String msg = ctx.getString(R.string.auth_failed); showToast(ctx, msg); } } catch (MalformedURLException e) { - if (toastAllowed) { + if (toastAllowed && ctx != null) { showToast(ctx, MessageFormat.format(ctx.getResources().getString(R.string.shared_string_action_template) + ": " + ctx.getResources().getString(R.string.shared_string_unexpected_error), userOperation)); } } catch (IOException e) { - if (toastAllowed) { + if (toastAllowed && ctx != null) { showToast(ctx, MessageFormat.format(ctx.getResources().getString(R.string.shared_string_action_template) + ": " + ctx.getResources().getString(R.string.shared_string_io_error), userOperation)); } @@ -277,18 +371,23 @@ public class AndroidNetworkUtils { return res; } - public static String downloadFile(@NonNull String url, @NonNull File fileToSave) { + public static String downloadFile(@NonNull String url, @NonNull File fileToSave, boolean gzip, @Nullable IProgress progress) { String error = null; try { URLConnection connection = NetworkUtils.getHttpURLConnection(url); connection.setConnectTimeout(CONNECTION_TIMEOUT); connection.setReadTimeout(CONNECTION_TIMEOUT); - BufferedInputStream inputStream = new BufferedInputStream(connection.getInputStream(), 8 * 1024); + if (gzip) { + connection.setRequestProperty("Accept-Encoding", "deflate, gzip"); + } + InputStream inputStream = gzip + ? new GZIPInputStream(connection.getInputStream()) + : new BufferedInputStream(connection.getInputStream(), 8 * 1024); fileToSave.getParentFile().mkdirs(); OutputStream stream = null; try { stream = new FileOutputStream(fileToSave); - Algorithms.streamCopy(inputStream, stream); + Algorithms.streamCopy(inputStream, stream, progress, 1024); stream.flush(); } finally { Algorithms.closeStream(inputStream); @@ -307,12 +406,17 @@ public class AndroidNetworkUtils { private static final String BOUNDARY = "CowMooCowMooCowCowCow"; public static String uploadFile(@NonNull String urlText, @NonNull File file, boolean gzip, - @NonNull Map additionalParams, @Nullable Map headers) throws IOException { - return uploadFile(urlText, new FileInputStream(file), file.getName(), gzip, additionalParams, headers); + @NonNull Map additionalParams, + @Nullable Map headers, + @Nullable IProgress progress) throws IOException { + return uploadFile(urlText, new FileInputStream(file), file.getName(), gzip, additionalParams, headers, progress); } - public static String uploadFile(@NonNull String urlText, @NonNull InputStream inputStream, @NonNull String fileName, boolean gzip, - Map additionalParams, @Nullable Map headers) { + public static String uploadFile(@NonNull String urlText, @NonNull InputStream inputStream, + @NonNull String fileName, boolean gzip, + @NonNull Map additionalParams, + @Nullable Map headers, + @Nullable IProgress progress) { URL url; try { boolean firstPrm = !urlText.contains("?"); @@ -350,11 +454,11 @@ public class AndroidNetworkUtils { ous.flush(); if (gzip) { GZIPOutputStream gous = new GZIPOutputStream(ous, 1024); - Algorithms.streamCopy(bis, gous); + Algorithms.streamCopy(bis, gous, progress, 1024); gous.flush(); gous.finish(); } else { - Algorithms.streamCopy(bis, ous); + Algorithms.streamCopy(bis, ous, progress, 1024); } ous.write(("\r\n--" + BOUNDARY + "--\r\n").getBytes()); @@ -406,8 +510,19 @@ public class AndroidNetworkUtils { @NonNull protected Map doInBackground(Void... v) { Map errors = new HashMap<>(); - for (File file : files) { + for (final File file : files) { + final int[] progressValue = {0}; publishProgress(file, 0); + IProgress progress = null; + if (callback != null) { + progress = new NetworkProgress() { + @Override + public void progress(int deltaWork) { + progressValue[0] += deltaWork; + publishProgress(file, progressValue[0]); + } + }; + } try { Map params = new HashMap<>(parameters); if (callback != null) { @@ -416,14 +531,14 @@ public class AndroidNetworkUtils { params.putAll(additionalParams); } } - String res = uploadFile(url, file, gzip, params, headers); + String res = uploadFile(url, file, gzip, params, headers, progress); if (res != null) { errors.put(file, res); } } catch (Exception e) { errors.put(file, e.getMessage()); } - publishProgress(file, 100); + publishProgress(file, Integer.MAX_VALUE); } return errors; } @@ -484,4 +599,39 @@ public class AndroidNetworkUtils { return post; } } + + private abstract static class NetworkProgress implements IProgress { + @Override + public void startTask(String taskName, int work) { + } + + @Override + public void startWork(int work) { + } + + @Override + public abstract void progress(int deltaWork); + + @Override + public void remaining(int remainingWork) { + } + + @Override + public void finishTask() { + } + + @Override + public boolean isIndeterminate() { + return false; + } + + @Override + public boolean isInterrupted() { + return false; + } + + @Override + public void setGeneralProgress(String genProgress) { + } + } } diff --git a/OsmAnd/src/net/osmand/AndroidUtils.java b/OsmAnd/src/net/osmand/AndroidUtils.java index 4f0729d355..96e44c652e 100644 --- a/OsmAnd/src/net/osmand/AndroidUtils.java +++ b/OsmAnd/src/net/osmand/AndroidUtils.java @@ -22,6 +22,8 @@ import android.graphics.drawable.ShapeDrawable; import android.graphics.drawable.StateListDrawable; import android.net.Uri; import android.os.Build; +import android.os.Build.VERSION; +import android.os.Build.VERSION_CODES; import android.os.IBinder; import android.os.PowerManager; import android.os.StatFs; @@ -264,6 +266,11 @@ public class AndroidUtils { return ""; } + public static String getFreeSpace(Context ctx, File dir) { + long size = AndroidUtils.getAvailableSpace(dir); + return AndroidUtils.formatSize(ctx, size); + } + public static View findParentViewById(View view, int id) { ViewParent viewParent = view.getParent(); @@ -856,11 +863,39 @@ public class AndroidUtils { return result; } + public static long getAvailableSpace(@NonNull OsmandApplication app) { + return getAvailableSpace(app.getAppPath(null)); + } + + public static long getTotalSpace(@NonNull OsmandApplication app) { + return getTotalSpace(app.getAppPath(null)); + } + public static long getAvailableSpace(@Nullable File dir) { if (dir != null && dir.canRead()) { try { StatFs fs = new StatFs(dir.getAbsolutePath()); - return fs.getAvailableBlocksLong() * fs.getBlockSize(); + if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR2) { + return fs.getAvailableBlocksLong() * fs.getBlockSizeLong(); + } else { + return fs.getAvailableBlocks() * fs.getBlockSize(); + } + } catch (IllegalArgumentException e) { + LOG.error(e); + } + } + return -1; + } + + public static long getTotalSpace(@Nullable File dir) { + if (dir != null && dir.canRead()) { + try { + StatFs fs = new StatFs(dir.getAbsolutePath()); + if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR2) { + return fs.getBlockCountLong() * fs.getBlockSizeLong(); + } else { + return fs.getBlockCount() * fs.getBlockSize(); + } } catch (IllegalArgumentException e) { LOG.error(e); } @@ -887,13 +922,6 @@ public class AndroidUtils { return -1; } - public static float getUsedSpaceGb(File dir) { - if (dir.canRead()) { - return getTotalSpaceGb(dir) - getFreeSpaceGb(dir); - } - return -1; - } - public static CharSequence getStyledString(CharSequence baseString, CharSequence stringToInsertAndStyle, CharacterStyle baseStyle, CharacterStyle replaceStyle) { int indexOfPlaceholder = baseString.toString().indexOf(STRING_PLACEHOLDER); diff --git a/OsmAnd/src/net/osmand/plus/AnalyticsHelper.java b/OsmAnd/src/net/osmand/plus/AnalyticsHelper.java index e8d04254c3..52dff8e6d0 100644 --- a/OsmAnd/src/net/osmand/plus/AnalyticsHelper.java +++ b/OsmAnd/src/net/osmand/plus/AnalyticsHelper.java @@ -184,7 +184,8 @@ public class AnalyticsHelper extends SQLiteOpenHelper { String jsonStr = json.toString(); InputStream inputStream = new ByteArrayInputStream(jsonStr.getBytes()); - String res = AndroidNetworkUtils.uploadFile(ANALYTICS_UPLOAD_URL, inputStream, ANALYTICS_FILE_NAME, true, additionalData, null); + String res = AndroidNetworkUtils.uploadFile(ANALYTICS_UPLOAD_URL, inputStream, + ANALYTICS_FILE_NAME, true, additionalData, null, null); if (res != null) { return; } diff --git a/OsmAnd/src/net/osmand/plus/AppInitializer.java b/OsmAnd/src/net/osmand/plus/AppInitializer.java index af3ea4f475..07bb9084e8 100644 --- a/OsmAnd/src/net/osmand/plus/AppInitializer.java +++ b/OsmAnd/src/net/osmand/plus/AppInitializer.java @@ -31,6 +31,7 @@ import net.osmand.osm.MapPoiTypes; import net.osmand.plus.activities.LocalIndexHelper; import net.osmand.plus.activities.LocalIndexInfo; import net.osmand.plus.activities.SavingTrackHelper; +import net.osmand.plus.backup.BackupHelper; import net.osmand.plus.base.MapViewTrackingUtilities; import net.osmand.plus.download.DownloadActivity; import net.osmand.plus.download.ui.AbstractLoadLocalIndexTask; @@ -39,7 +40,6 @@ import net.osmand.plus.helpers.DayNightHelper; import net.osmand.plus.helpers.LockHelper; import net.osmand.plus.helpers.WaypointHelper; import net.osmand.plus.inapp.InAppPurchaseHelperImpl; -import net.osmand.plus.itinerary.ItineraryHelper; import net.osmand.plus.liveupdates.LiveUpdatesHelper; import net.osmand.plus.mapmarkers.MapMarkersDbHelper; import net.osmand.plus.mapmarkers.MapMarkersHelper; @@ -472,7 +472,7 @@ public class AppInitializer implements IProgress { app.osmOAuthHelper = startupInit(new OsmOAuthHelper(app), OsmOAuthHelper.class); app.oprAuthHelper = startupInit(new OprAuthHelper(app), OprAuthHelper.class); app.onlineRoutingHelper = startupInit(new OnlineRoutingHelper(app), OnlineRoutingHelper.class); - app.itineraryHelper = startupInit(new ItineraryHelper(app), ItineraryHelper.class); + app.backupHelper = startupInit(new BackupHelper(app), BackupHelper.class); initOpeningHoursParser(); } @@ -685,7 +685,7 @@ public class AppInitializer implements IProgress { // restore backuped favorites to normal file restoreBackupForFavoritesFiles(); notifyEvent(InitEvents.RESTORE_BACKUPS); - app.itineraryHelper.syncAllGroupsAsync(); + app.mapMarkersHelper.syncAllGroupsAsync(); app.searchUICore.initSearchUICore(); checkLiveUpdatesAlerts(); diff --git a/OsmAnd/src/net/osmand/plus/ContextMenuAdapter.java b/OsmAnd/src/net/osmand/plus/ContextMenuAdapter.java index 5fdea403d3..8b3fc01882 100644 --- a/OsmAnd/src/net/osmand/plus/ContextMenuAdapter.java +++ b/OsmAnd/src/net/osmand/plus/ContextMenuAdapter.java @@ -445,6 +445,9 @@ public class ContextMenuAdapter { ImageView imageView = (ImageView) convertView.findViewById(R.id.secondary_icon); imageView.setImageDrawable(drawable); imageView.setVisibility(View.VISIBLE); + if (secondaryDrawable == R.drawable.ic_action_additional_option) { + UiUtilities.rotateImageByLayoutDirection(imageView); + } } else { ImageView imageView = (ImageView) convertView.findViewById(R.id.secondary_icon); if (imageView != null) { diff --git a/OsmAnd/src/net/osmand/plus/FavouritesDbHelper.java b/OsmAnd/src/net/osmand/plus/FavouritesDbHelper.java index c90ecccc02..42cf1c0d2e 100644 --- a/OsmAnd/src/net/osmand/plus/FavouritesDbHelper.java +++ b/OsmAnd/src/net/osmand/plus/FavouritesDbHelper.java @@ -17,7 +17,7 @@ import net.osmand.data.FavouritePoint; import net.osmand.data.LatLon; import net.osmand.plus.GeocodingLookupService.AddressLookupRequest; import net.osmand.plus.mapmarkers.MapMarkersHelper; -import net.osmand.plus.itinerary.ItineraryGroup; +import net.osmand.plus.mapmarkers.MapMarkersGroup; import net.osmand.plus.api.SQLiteAPI.SQLiteConnection; import net.osmand.plus.api.SQLiteAPI.SQLiteCursor; import net.osmand.util.Algorithms; @@ -146,6 +146,14 @@ public class FavouritesDbHelper { } } + public long getLastUploadedTime() { + return context.getSettings().FAVORITES_LAST_UPLOADED_TIME.get(); + } + + public void setLastUploadedTime(long time) { + context.getSettings().FAVORITES_LAST_UPLOADED_TIME.set(time); + } + @Nullable public Drawable getColoredIconForGroup(String groupName) { String groupIdName = FavoriteGroup.convertDisplayNameToGroupIdName(context, groupName); @@ -272,15 +280,16 @@ public class FavouritesDbHelper { } private void runSyncWithMarkers(FavoriteGroup favGroup) { - ItineraryGroup group = context.getItineraryHelper().getMarkersGroup(favGroup); + MapMarkersHelper helper = context.getMapMarkersHelper(); + MapMarkersGroup group = helper.getMarkersGroup(favGroup); if (group != null) { - context.getItineraryHelper().runSynchronization(group); + helper.runSynchronization(group); } } private boolean removeFromMarkers(FavoriteGroup favGroup) { MapMarkersHelper helper = context.getMapMarkersHelper(); - ItineraryGroup group = context.getItineraryHelper().getMarkersGroup(favGroup); + MapMarkersGroup group = helper.getMarkersGroup(favGroup); if (group != null) { helper.removeMarkersGroup(group); return true; @@ -289,7 +298,8 @@ public class FavouritesDbHelper { } private void addToMarkers(FavoriteGroup favGroup) { - context.getItineraryHelper().addOrEnableGroup(favGroup); + MapMarkersHelper helper = context.getMapMarkersHelper(); + helper.addOrEnableGroup(favGroup); } private File getInternalFile() { diff --git a/OsmAnd/src/net/osmand/plus/GPXDatabase.java b/OsmAnd/src/net/osmand/plus/GPXDatabase.java index 385ec79484..f1e5f64099 100644 --- a/OsmAnd/src/net/osmand/plus/GPXDatabase.java +++ b/OsmAnd/src/net/osmand/plus/GPXDatabase.java @@ -18,7 +18,7 @@ import androidx.annotation.Nullable; public class GPXDatabase { - private static final int DB_VERSION = 11; + private static final int DB_VERSION = 12; private static final String DB_NAME = "gpx_database"; private static final String GPX_TABLE_NAME = "gpxTable"; @@ -48,6 +48,7 @@ public class GPXDatabase { private static final String GPX_COL_COLOR = "color"; private static final String GPX_COL_FILE_LAST_MODIFIED_TIME = "fileLastModifiedTime"; + private static final String GPX_COL_FILE_LAST_UPLOADED_TIME = "fileLastUploadedTime"; private static final String GPX_COL_SPLIT_TYPE = "splitType"; private static final String GPX_COL_SPLIT_INTERVAL = "splitInterval"; @@ -98,6 +99,7 @@ public class GPXDatabase { GPX_COL_WPT_POINTS + " int, " + GPX_COL_COLOR + " TEXT, " + GPX_COL_FILE_LAST_MODIFIED_TIME + " long, " + + GPX_COL_FILE_LAST_UPLOADED_TIME + " long, " + GPX_COL_SPLIT_TYPE + " int, " + GPX_COL_SPLIT_INTERVAL + " double, " + GPX_COL_API_IMPORTED + " int, " + // 1 = true, 0 = false @@ -133,6 +135,7 @@ public class GPXDatabase { GPX_COL_WPT_POINTS + ", " + GPX_COL_COLOR + ", " + GPX_COL_FILE_LAST_MODIFIED_TIME + ", " + + GPX_COL_FILE_LAST_UPLOADED_TIME + ", " + GPX_COL_SPLIT_TYPE + ", " + GPX_COL_SPLIT_INTERVAL + ", " + GPX_COL_API_IMPORTED + ", " + @@ -184,6 +187,7 @@ public class GPXDatabase { private int splitType; private double splitInterval; private long fileLastModifiedTime; + private long fileLastUploadedTime; private boolean apiImported; private boolean showAsMarkers; private boolean joinSegments; @@ -200,6 +204,11 @@ public class GPXDatabase { this.color = color; } + public GpxDataItem(File file, long fileLastUploadedTime) { + this.file = file; + this.fileLastUploadedTime = fileLastUploadedTime; + } + public GpxDataItem(File file, @NonNull GPXFile gpxFile) { this.file = file; readGpxParams(gpxFile); @@ -263,6 +272,10 @@ public class GPXDatabase { return fileLastModifiedTime; } + public long getFileLastUploadedTime() { + return fileLastUploadedTime; + } + public int getSplitType() { return splitType; } @@ -441,10 +454,13 @@ public class GPXDatabase { db.execSQL("UPDATE " + GPX_TABLE_NAME + " SET " + GPX_COL_SHOW_START_FINISH + " = ? " + "WHERE " + GPX_COL_SHOW_START_FINISH + " IS NULL", new Object[]{1}); } + if (oldVersion < 12) { + db.execSQL("ALTER TABLE " + GPX_TABLE_NAME + " ADD " + GPX_COL_FILE_LAST_UPLOADED_TIME + " long"); + } db.execSQL("CREATE INDEX IF NOT EXISTS " + GPX_INDEX_NAME_DIR + " ON " + GPX_TABLE_NAME + " (" + GPX_COL_NAME + ", " + GPX_COL_DIR + ");"); } - private boolean updateLastModifiedTime(GpxDataItem item) { + private boolean updateLastModifiedTime(@NonNull GpxDataItem item) { SQLiteConnection db = openConnection(false); if (db != null) { try { @@ -464,6 +480,25 @@ public class GPXDatabase { return false; } + public boolean updateLastUploadedTime(@NonNull GpxDataItem item, long fileLastUploadedTime) { + SQLiteConnection db = openConnection(false); + if (db != null) { + try { + String fileName = getFileName(item.file); + String fileDir = getFileDir(item.file); + db.execSQL("UPDATE " + GPX_TABLE_NAME + " SET " + + GPX_COL_FILE_LAST_UPLOADED_TIME + " = ? " + + " WHERE " + GPX_COL_NAME + " = ? AND " + GPX_COL_DIR + " = ?", + new Object[] { fileLastUploadedTime, fileName, fileDir }); + item.fileLastUploadedTime = fileLastUploadedTime; + } finally { + db.close(); + } + return true; + } + return false; + } + public boolean rename(@Nullable GpxDataItem item, File currentFile, File newFile) { SQLiteConnection db = openConnection(false); if (db != null){ @@ -721,11 +756,11 @@ public class GPXDatabase { String gradientScaleType = item.gradientScaleType != null ? item.gradientScaleType.getTypeName() : null; if (a != null) { db.execSQL( - "INSERT INTO " + GPX_TABLE_NAME + " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", + "INSERT INTO " + GPX_TABLE_NAME + " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", new Object[] {fileName, fileDir, a.totalDistance, a.totalTracks, a.startTime, a.endTime, a.timeSpan, a.timeMoving, a.totalDistanceMoving, a.diffElevationUp, a.diffElevationDown, a.avgElevation, a.minElevation, a.maxElevation, a.maxSpeed, a.avgSpeed, a.points, a.wptPoints, - color, item.file.lastModified(), item.splitType, item.splitInterval, item.apiImported ? 1 : 0, + color, item.file.lastModified(), item.fileLastUploadedTime, item.splitType, item.splitInterval, item.apiImported ? 1 : 0, Algorithms.encodeStringSet(item.analysis.wptCategoryNames), item.showAsMarkers ? 1 : 0, item.joinSegments ? 1 : 0, item.showArrows ? 1 : 0, item.showStartFinish ? 1 : 0, item.width, item.gradientSpeedPalette, item.gradientAltitudePalette, item.gradientSlopePalette, gradientScaleType}); @@ -735,6 +770,7 @@ public class GPXDatabase { GPX_COL_DIR + ", " + GPX_COL_COLOR + ", " + GPX_COL_FILE_LAST_MODIFIED_TIME + ", " + + GPX_COL_FILE_LAST_UPLOADED_TIME + ", " + GPX_COL_SPLIT_TYPE + ", " + GPX_COL_SPLIT_INTERVAL + ", " + GPX_COL_API_IMPORTED + ", " + @@ -747,8 +783,8 @@ public class GPXDatabase { GPX_COL_GRADIENT_ALTITUDE_COLOR + ", " + GPX_COL_GRADIENT_SLOPE_COLOR + ", " + GPX_COL_GRADIENT_SCALE_TYPE + - ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", - new Object[] {fileName, fileDir, color, 0, item.splitType, item.splitInterval, + ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", + new Object[] {fileName, fileDir, color, 0, item.fileLastUploadedTime, item.splitType, item.splitInterval, item.apiImported ? 1 : 0, item.showAsMarkers ? 1 : 0, item.joinSegments ? 1 : 0, item.showArrows ? 1 : 0, item.showStartFinish ? 1 : 0, item.width, Algorithms.gradientPaletteToString(item.gradientSpeedPalette), @@ -828,19 +864,20 @@ public class GPXDatabase { int wptPoints = (int)query.getInt(17); String color = query.getString(18); long fileLastModifiedTime = query.getLong(19); - int splitType = (int)query.getInt(20); - double splitInterval = query.getDouble(21); - boolean apiImported = query.getInt(22) == 1; - String wptCategoryNames = query.getString(23); - boolean showAsMarkers = query.getInt(24) == 1; - boolean joinSegments = query.getInt(25) == 1; - boolean showArrows = query.getInt(26) == 1; - boolean showStartFinish = query.getInt(27) == 1; - String width = query.getString(28); - String gradientSpeedPalette = query.getString(29); - String gradientAltitudePalette = query.getString(30); - String gradientSlopePalette = query.getString(31); - String gradientScaleType = query.getString(32); + long fileLastUploadedTime = query.getLong(20); + int splitType = (int)query.getInt(21); + double splitInterval = query.getDouble(22); + boolean apiImported = query.getInt(23) == 1; + String wptCategoryNames = query.getString(24); + boolean showAsMarkers = query.getInt(25) == 1; + boolean joinSegments = query.getInt(26) == 1; + boolean showArrows = query.getInt(27) == 1; + boolean showStartFinish = query.getInt(28) == 1; + String width = query.getString(29); + String gradientSpeedPalette = query.getString(30); + String gradientAltitudePalette = query.getString(31); + String gradientSlopePalette = query.getString(32); + String gradientScaleType = query.getString(33); GPXTrackAnalysis a = new GPXTrackAnalysis(); a.totalDistance = totalDistance; @@ -873,6 +910,7 @@ public class GPXDatabase { GpxDataItem item = new GpxDataItem(new File(dir, fileName), a); item.color = parseColor(color); item.fileLastModifiedTime = fileLastModifiedTime; + item.fileLastUploadedTime = fileLastUploadedTime; item.splitType = splitType; item.splitInterval = splitInterval; item.apiImported = apiImported; diff --git a/OsmAnd/src/net/osmand/plus/GpxDbHelper.java b/OsmAnd/src/net/osmand/plus/GpxDbHelper.java index 21154d5819..6bfc639f70 100644 --- a/OsmAnd/src/net/osmand/plus/GpxDbHelper.java +++ b/OsmAnd/src/net/osmand/plus/GpxDbHelper.java @@ -78,6 +78,12 @@ public class GpxDbHelper { return res; } + public boolean updateLastUploadedTime(GpxDataItem item, long fileLastUploadedTime) { + boolean res = db.updateLastUploadedTime(item, fileLastUploadedTime); + putToCache(item); + return res; + } + public boolean updateGradientScalePalette(@NonNull GpxDataItem item, @NonNull GradientScaleType gradientScaleType, int[] palette) { boolean res = db.updateGradientScaleColor(item, gradientScaleType, palette); putToCache(item); diff --git a/OsmAnd/src/net/osmand/plus/GpxSelectionHelper.java b/OsmAnd/src/net/osmand/plus/GpxSelectionHelper.java index 3c95d75e9f..0eba1b2b1a 100644 --- a/OsmAnd/src/net/osmand/plus/GpxSelectionHelper.java +++ b/OsmAnd/src/net/osmand/plus/GpxSelectionHelper.java @@ -30,7 +30,8 @@ import net.osmand.plus.helpers.GpxUiHelper.GPXDataSetType; import net.osmand.plus.helpers.GpxUiHelper.GPXInfo; import net.osmand.plus.helpers.SearchHistoryHelper; import net.osmand.plus.helpers.enums.MetricsConstants; -import net.osmand.plus.itinerary.ItineraryGroup; +import net.osmand.plus.mapmarkers.MapMarkersGroup; +import net.osmand.plus.mapmarkers.MapMarkersHelper; import net.osmand.plus.routing.GPXRouteParams.GPXRouteParamsBuilder; import net.osmand.plus.track.GpxSplitType; import net.osmand.util.Algorithms; @@ -291,10 +292,12 @@ public class GpxSelectionHelper { return group; } - private String getGroupName(GPXFile g) { + public String getGroupName(GPXFile g) { String name = g.path; if (g.showCurrentTrack) { name = getString(R.string.shared_string_currently_recording_track); + } else if (Algorithms.isEmpty(name)) { + name = getString(R.string.current_route); } else { int i = name.lastIndexOf('/'); if (i >= 0) { @@ -795,7 +798,7 @@ public class GpxSelectionHelper { boolean addToHistory) { GpxDataItem dataItem = app.getGpxDbHelper().getItem(new File(gpx.path)); if (canAddToMarkers && show && dataItem != null && dataItem.isShowAsMarkers()) { - app.getItineraryHelper().addOrEnableGroup(gpx); + app.getMapMarkersHelper().addOrEnableGroup(gpx); } return selectGpxFile(gpx, dataItem, show, notShowNavigationDialog, syncGroup, selectedByUser, addToHistory); } @@ -822,9 +825,10 @@ public class GpxSelectionHelper { } private void syncGpxWithMarkers(GPXFile gpxFile) { - ItineraryGroup group = app.getItineraryHelper().getMarkersGroup(gpxFile); + MapMarkersHelper mapMarkersHelper = app.getMapMarkersHelper(); + MapMarkersGroup group = mapMarkersHelper.getMarkersGroup(gpxFile); if (group != null) { - app.getItineraryHelper().runSynchronization(group); + mapMarkersHelper.runSynchronization(group); } } diff --git a/OsmAnd/src/net/osmand/plus/OsmandApplication.java b/OsmAnd/src/net/osmand/plus/OsmandApplication.java index 72e4266f35..cd3fa4b5ad 100644 --- a/OsmAnd/src/net/osmand/plus/OsmandApplication.java +++ b/OsmAnd/src/net/osmand/plus/OsmandApplication.java @@ -52,6 +52,7 @@ import net.osmand.plus.activities.SavingTrackHelper; import net.osmand.plus.activities.actions.OsmAndDialogs; import net.osmand.plus.api.SQLiteAPI; import net.osmand.plus.api.SQLiteAPIImpl; +import net.osmand.plus.backup.BackupHelper; import net.osmand.plus.base.MapViewTrackingUtilities; import net.osmand.plus.download.DownloadIndexesThread; import net.osmand.plus.download.DownloadService; @@ -66,7 +67,6 @@ import net.osmand.plus.helpers.WaypointHelper; import net.osmand.plus.helpers.enums.DrivingRegion; import net.osmand.plus.helpers.enums.MetricsConstants; import net.osmand.plus.inapp.InAppPurchaseHelper; -import net.osmand.plus.itinerary.ItineraryHelper; import net.osmand.plus.mapmarkers.MapMarkersDbHelper; import net.osmand.plus.mapmarkers.MapMarkersHelper; import net.osmand.plus.measurementtool.MeasurementEditingContext; @@ -168,7 +168,7 @@ public class OsmandApplication extends MultiDexApplication { OprAuthHelper oprAuthHelper; MeasurementEditingContext measurementEditingContext; OnlineRoutingHelper onlineRoutingHelper; - ItineraryHelper itineraryHelper; + BackupHelper backupHelper; private Map customRoutingConfigs = new ConcurrentHashMap<>(); private File externalStorageDirectory; @@ -470,8 +470,8 @@ public class OsmandApplication extends MultiDexApplication { return onlineRoutingHelper; } - public ItineraryHelper getItineraryHelper() { - return itineraryHelper; + public BackupHelper getBackupHelper() { + return backupHelper; } public TransportRoutingHelper getTransportRoutingHelper() { diff --git a/OsmAnd/src/net/osmand/plus/ProgressImplementation.java b/OsmAnd/src/net/osmand/plus/ProgressImplementation.java index 03d2114c7b..9e673bacdc 100644 --- a/OsmAnd/src/net/osmand/plus/ProgressImplementation.java +++ b/OsmAnd/src/net/osmand/plus/ProgressImplementation.java @@ -5,6 +5,7 @@ import android.app.ProgressDialog; import android.content.Context; import android.content.DialogInterface; import android.content.DialogInterface.OnCancelListener; +import android.content.res.Resources; import android.os.Handler; import android.os.Message; import android.widget.ProgressBar; @@ -204,7 +205,8 @@ public class ProgressImplementation implements IProgress { work = -1; progress = 0; if (taskName != null) { - message = context.getResources().getString(R.string.finished_task) +" : "+ taskName; //$NON-NLS-1$ + Resources resources = context.getResources(); + message = resources.getString(R.string.ltr_or_rtl_combine_via_colon, resources.getString(R.string.finished_task), taskName); mViewUpdateHandler.sendEmptyMessage(HANDLER_START_TASK); } } diff --git a/OsmAnd/src/net/osmand/plus/UiUtilities.java b/OsmAnd/src/net/osmand/plus/UiUtilities.java index a7c0223c22..cba86b7f8b 100644 --- a/OsmAnd/src/net/osmand/plus/UiUtilities.java +++ b/OsmAnd/src/net/osmand/plus/UiUtilities.java @@ -442,15 +442,14 @@ public class UiUtilities { } catch (Throwable e) { } } - public static void rotateImageByLayoutDirection(ImageView image, int layoutDirection) { + public static void rotateImageByLayoutDirection(ImageView image) { if (image == null) { return; } - int rotation = layoutDirection == ViewCompat.LAYOUT_DIRECTION_RTL ? 180 : 0; + int rotation = AndroidUtils.getLayoutDirection(image.getContext()) == ViewCompat.LAYOUT_DIRECTION_RTL ? 180 : 0; image.setRotationY(rotation); } - public static void updateCustomRadioButtons(Context app, View buttonsView, boolean nightMode, CustomRadioButtonType buttonType) { int activeColor = ContextCompat.getColor(app, nightMode diff --git a/OsmAnd/src/net/osmand/plus/activities/EditFavoriteGroupDialogFragment.java b/OsmAnd/src/net/osmand/plus/activities/EditFavoriteGroupDialogFragment.java index e625212ee7..001868e7b4 100644 --- a/OsmAnd/src/net/osmand/plus/activities/EditFavoriteGroupDialogFragment.java +++ b/OsmAnd/src/net/osmand/plus/activities/EditFavoriteGroupDialogFragment.java @@ -28,7 +28,7 @@ import net.osmand.AndroidUtils; import net.osmand.plus.FavouritesDbHelper; import net.osmand.plus.FavouritesDbHelper.FavoriteGroup; import net.osmand.plus.mapmarkers.MapMarkersHelper; -import net.osmand.plus.itinerary.ItineraryGroup; +import net.osmand.plus.mapmarkers.MapMarkersGroup; import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; import net.osmand.plus.UiUtilities; @@ -179,7 +179,7 @@ public class EditFavoriteGroupDialogFragment extends MenuBottomSheetDialogFragme final MapMarkersHelper markersHelper = app.getMapMarkersHelper(); final FavoriteGroup favGroup = this.group; - final ItineraryGroup markersGr = app.getItineraryHelper().getMarkersGroup(this.group); + final MapMarkersGroup markersGr = markersHelper.getMarkersGroup(this.group); final boolean synced = markersGr != null; BaseBottomSheetItem markersGroupItem = new SimpleBottomSheetItem.Builder() @@ -192,7 +192,7 @@ public class EditFavoriteGroupDialogFragment extends MenuBottomSheetDialogFragme if (synced) { markersHelper.removeMarkersGroup(markersGr); } else { - app.getItineraryHelper().addOrEnableGroup(favGroup); + markersHelper.addOrEnableGroup(favGroup); } dismiss(); MapActivity.launchMapActivityMoveToTop(getActivity()); diff --git a/OsmAnd/src/net/osmand/plus/activities/FavoritesTreeFragment.java b/OsmAnd/src/net/osmand/plus/activities/FavoritesTreeFragment.java index 98304247a4..a24859fa30 100644 --- a/OsmAnd/src/net/osmand/plus/activities/FavoritesTreeFragment.java +++ b/OsmAnd/src/net/osmand/plus/activities/FavoritesTreeFragment.java @@ -526,7 +526,7 @@ public class FavoritesTreeFragment extends OsmandExpandableListFragment implemen for (Map.Entry> entry : favoritesSelected.entrySet()) { FavoriteGroup group = helper.getGroup(entry.getKey()); if (group != null && entry.getValue().size() == group.getPoints().size()) { - getMyApplication().getItineraryHelper().addOrEnableGroup(group); + markersHelper.addOrEnableGroup(group); } else { for (FavouritePoint fp : entry.getValue()) { points.add(new LatLon(fp.getLatitude(), fp.getLongitude())); diff --git a/OsmAnd/src/net/osmand/plus/activities/LocalIndexHelper.java b/OsmAnd/src/net/osmand/plus/activities/LocalIndexHelper.java index c1de43084e..51d6125ed7 100644 --- a/OsmAnd/src/net/osmand/plus/activities/LocalIndexHelper.java +++ b/OsmAnd/src/net/osmand/plus/activities/LocalIndexHelper.java @@ -13,6 +13,7 @@ import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; import net.osmand.plus.SQLiteTileSource; import net.osmand.plus.Version; +import net.osmand.plus.download.SrtmDownloadItem; import net.osmand.plus.download.ui.AbstractLoadLocalIndexTask; import net.osmand.plus.voice.JSMediaCommandPlayerImpl; import net.osmand.plus.voice.JSTTSCommandPlayerImpl; @@ -143,8 +144,6 @@ public class LocalIndexHelper { return null; } - - public List getLocalIndexInfos(String downloadName) { List list = new ArrayList<>(); LocalIndexInfo info = getLocalIndexInfo(LocalIndexType.MAP_DATA, downloadName, false, false); @@ -313,7 +312,7 @@ public class LocalIndexHelper { } } } - + private void loadTravelData(File mapPath, List result, AbstractLoadLocalIndexTask loadTask) { if (mapPath.canRead()) { for (File mapFile : listFilesSorted(mapPath)) { @@ -333,14 +332,15 @@ public class LocalIndexHelper { if (mapPath.canRead()) { for (File mapFile : listFilesSorted(mapPath)) { if (mapFile.isFile() && mapFile.getName().endsWith(IndexConstants.BINARY_MAP_INDEX_EXT)) { + String fileName = mapFile.getName(); LocalIndexType lt = LocalIndexType.MAP_DATA; - if (mapFile.getName().endsWith(IndexConstants.BINARY_SRTM_MAP_INDEX_EXT)) { + if (SrtmDownloadItem.isSrtmFile(fileName)) { lt = LocalIndexType.SRTM_DATA; - } else if (mapFile.getName().endsWith(IndexConstants.BINARY_WIKI_MAP_INDEX_EXT)) { + } else if (fileName.endsWith(IndexConstants.BINARY_WIKI_MAP_INDEX_EXT)) { lt = LocalIndexType.WIKI_DATA; } LocalIndexInfo info = new LocalIndexInfo(lt, mapFile, backup, app); - if (loadedMaps.containsKey(mapFile.getName()) && !backup) { + if (loadedMaps.containsKey(fileName) && !backup) { info.setLoaded(true); } updateDescription(info); @@ -403,7 +403,7 @@ public class LocalIndexHelper { if (fileName.endsWith(IndexConstants.SQLITE_EXT)) { return fileName.substring(0, fileName.length() - IndexConstants.SQLITE_EXT.length()); } - if (localIndexInfo.getType() == TRAVEL_DATA && + if (localIndexInfo.getType() == TRAVEL_DATA && fileName.endsWith(IndexConstants.BINARY_WIKIVOYAGE_MAP_INDEX_EXT)) { return fileName.substring(0, fileName.length() - IndexConstants.BINARY_WIKIVOYAGE_MAP_INDEX_EXT.length()); } @@ -430,5 +430,4 @@ public class LocalIndexHelper { return fileName; } } - } diff --git a/OsmAnd/src/net/osmand/plus/activities/MapActivityKeyListener.java b/OsmAnd/src/net/osmand/plus/activities/MapActivityKeyListener.java index 33ce52296b..4b99e84350 100644 --- a/OsmAnd/src/net/osmand/plus/activities/MapActivityKeyListener.java +++ b/OsmAnd/src/net/osmand/plus/activities/MapActivityKeyListener.java @@ -94,6 +94,12 @@ public class MapActivityKeyListener implements KeyEvent.Callback { mapActivity.getMapViewTrackingUtilities().backToLocationImpl(); } else if (keyCode == KeyEvent.KEYCODE_D) { mapActivity.getMapViewTrackingUtilities().switchRotateMapMode(); + } else if (keyCode == KeyEvent.KEYCODE_MINUS) { + mapActivity.changeZoom(-1); + return true; + } else if (keyCode == KeyEvent.KEYCODE_PLUS || keyCode == KeyEvent.KEYCODE_EQUALS) { + mapActivity.changeZoom(1); + return true; } else if (mapScrollHelper.isAvailableKeyCode(keyCode)) { return mapScrollHelper.onKeyUp(keyCode, event); } else if (settings.EXTERNAL_INPUT_DEVICE.get() == PARROT_EXTERNAL_DEVICE) { @@ -121,13 +127,7 @@ public class MapActivityKeyListener implements KeyEvent.Callback { return true; } } else if (settings.EXTERNAL_INPUT_DEVICE.get() == GENERIC_EXTERNAL_DEVICE) { - if (keyCode == KeyEvent.KEYCODE_MINUS) { - mapActivity.changeZoom(-1); - return true; - } else if (keyCode == KeyEvent.KEYCODE_PLUS || keyCode == KeyEvent.KEYCODE_EQUALS) { - mapActivity.changeZoom(1); - return true; - } + // currently doesn't process specific commands } else if (OsmandPlugin.onMapActivityKeyUp(mapActivity, keyCode)) { return true; } diff --git a/OsmAnd/src/net/osmand/plus/activities/PluginInfoFragment.java b/OsmAnd/src/net/osmand/plus/activities/PluginInfoFragment.java index ced666e32b..5d32ee228d 100644 --- a/OsmAnd/src/net/osmand/plus/activities/PluginInfoFragment.java +++ b/OsmAnd/src/net/osmand/plus/activities/PluginInfoFragment.java @@ -109,7 +109,7 @@ public class PluginInfoFragment extends BaseOsmAndFragment implements PluginStat } } }); - UiUtilities.rotateImageByLayoutDirection(closeButton, AndroidUtils.getLayoutDirection(app)); + UiUtilities.rotateImageByLayoutDirection(closeButton); Drawable pluginImage = plugin.getAssetResourceImage(); if (pluginImage != null) { diff --git a/OsmAnd/src/net/osmand/plus/activities/PluginsFragment.java b/OsmAnd/src/net/osmand/plus/activities/PluginsFragment.java index 04666e9607..54439daefa 100644 --- a/OsmAnd/src/net/osmand/plus/activities/PluginsFragment.java +++ b/OsmAnd/src/net/osmand/plus/activities/PluginsFragment.java @@ -101,7 +101,7 @@ public class PluginsFragment extends BaseOsmAndFragment implements PluginStateLi } } }); - UiUtilities.rotateImageByLayoutDirection(closeButton, AndroidUtils.getLayoutDirection(app)); + UiUtilities.rotateImageByLayoutDirection(closeButton); adapter = new PluginsListAdapter(requireContext()); diff --git a/OsmAnd/src/net/osmand/plus/audionotes/AudioVideoNotesPlugin.java b/OsmAnd/src/net/osmand/plus/audionotes/AudioVideoNotesPlugin.java index 0bf4bb55fb..2a09ffe3e9 100644 --- a/OsmAnd/src/net/osmand/plus/audionotes/AudioVideoNotesPlugin.java +++ b/OsmAnd/src/net/osmand/plus/audionotes/AudioVideoNotesPlugin.java @@ -22,7 +22,6 @@ import android.media.MediaRecorder; import android.media.SoundPool; import android.net.Uri; import android.os.Build; -import android.os.StatFs; import android.provider.MediaStore; import android.view.Display; import android.view.KeyEvent; @@ -1607,13 +1606,7 @@ public class AudioVideoNotesPlugin extends OsmandPlugin { double bitrate = (((p.videoBitRate + p.audioBitRate) / 8f) * 60f) / (1 << 30); // gigabytes per minute double clipSpace = bitrate * AV_RS_CLIP_LENGTH.get(); double storageSize = AV_RS_STORAGE_SIZE.get(); - - double availableSpace = storageSize; - File dir = app.getAppPath("").getParentFile(); - if (dir.canRead()) { - StatFs fs = new StatFs(dir.getAbsolutePath()); - availableSpace = (double) (fs.getAvailableBlocks()) * fs.getBlockSize() / (1 << 30) - clipSpace; - } + double availableSpace = (double) AndroidUtils.getAvailableSpace(app) / (1 << 30) - clipSpace; if (usedSpace + clipSpace > storageSize || clipSpace > availableSpace) { Arrays.sort(files, new Comparator() { diff --git a/OsmAnd/src/net/osmand/plus/audionotes/MultimediaNotesFragment.java b/OsmAnd/src/net/osmand/plus/audionotes/MultimediaNotesFragment.java index 3123b81c9b..5481908653 100644 --- a/OsmAnd/src/net/osmand/plus/audionotes/MultimediaNotesFragment.java +++ b/OsmAnd/src/net/osmand/plus/audionotes/MultimediaNotesFragment.java @@ -11,7 +11,6 @@ import android.media.CamcorderProfile; import android.media.MediaRecorder; import android.os.Build; import android.os.Bundle; -import android.os.StatFs; import android.text.SpannableString; import android.view.LayoutInflater; import android.view.View; @@ -42,7 +41,6 @@ import net.osmand.plus.widgets.style.CustomTypefaceSpan; import org.apache.commons.logging.Log; -import java.io.File; import java.util.ArrayList; import java.util.List; @@ -381,16 +379,7 @@ public class MultimediaNotesFragment extends BaseSettingsFragment implements Cop private void setupStorageSizePref(AudioVideoNotesPlugin plugin) { ListPreferenceEx storageSize = (ListPreferenceEx) findPreference(plugin.AV_RS_STORAGE_SIZE.getId()); - File dir = app.getAppPath("").getParentFile(); - long size = 0; - if (dir.canRead()) { - try { - StatFs fs = new StatFs(dir.getAbsolutePath()); - size = ((long) fs.getBlockSize() * (long) fs.getBlockCount()) / (1 << 30); - } catch (IllegalArgumentException e) { - log.error(e); - } - } + long size = AndroidUtils.getTotalSpace(app) / (1 << 30); if (size > 0) { int value = 1; ArrayList gbList = new ArrayList<>(); diff --git a/OsmAnd/src/net/osmand/plus/backup/BackupHelper.java b/OsmAnd/src/net/osmand/plus/backup/BackupHelper.java new file mode 100644 index 0000000000..326315b7d1 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/backup/BackupHelper.java @@ -0,0 +1,580 @@ +package net.osmand.plus.backup; + +import android.annotation.SuppressLint; +import android.os.AsyncTask; +import android.provider.Settings; +import android.util.Pair; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.WorkerThread; + +import net.osmand.AndroidNetworkUtils; +import net.osmand.AndroidNetworkUtils.OnFilesDownloadCallback; +import net.osmand.AndroidNetworkUtils.OnFilesUploadCallback; +import net.osmand.AndroidNetworkUtils.OnRequestResultListener; +import net.osmand.AndroidNetworkUtils.OnSendRequestsListener; +import net.osmand.AndroidNetworkUtils.Request; +import net.osmand.AndroidNetworkUtils.RequestResponse; +import net.osmand.AndroidUtils; +import net.osmand.IndexConstants; +import net.osmand.plus.FavouritesDbHelper; +import net.osmand.plus.GPXDatabase.GpxDataItem; +import net.osmand.plus.GpxDbHelper; +import net.osmand.plus.OsmandApplication; +import net.osmand.plus.inapp.InAppPurchaseHelper; +import net.osmand.plus.inapp.InAppPurchases.InAppSubscription; +import net.osmand.plus.settings.backend.OsmandSettings; +import net.osmand.util.Algorithms; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.File; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class BackupHelper { + + private final OsmandApplication app; + private final OsmandSettings settings; + private final FavouritesDbHelper favouritesHelper; + private final GpxDbHelper gpxHelper; + + private static final String SERVER_URL = "https://osmand.net"; + + private static final String USER_REGISTER_URL = SERVER_URL + "/userdata/user-register"; + private static final String DEVICE_REGISTER_URL = SERVER_URL + "/userdata/device-register"; + private static final String UPLOAD_FILE_URL = SERVER_URL + "/userdata/upload-file"; + private static final String LIST_FILES_URL = SERVER_URL + "/userdata/list-files"; + private static final String DOWNLOAD_FILE_URL = SERVER_URL + "/userdata/download-file"; + private static final String DELETE_FILE_URL = SERVER_URL + "/userdata/delete-file"; + + public final static int STATUS_SUCCESS = 0; + public final static int STATUS_PARSE_JSON_ERROR = 1; + public final static int STATUS_EMPTY_RESPONSE_ERROR = 2; + public final static int STATUS_SERVER_ERROR = 3; + + public interface OnResultListener { + void onResult(int status, @Nullable String message, @Nullable JSONObject json); + } + + public interface OnRegisterUserListener { + void onRegisterUser(int status, @Nullable String message); + } + + public interface OnRegisterDeviceListener { + void onRegisterDevice(int status, @Nullable String message); + } + + public interface OnDownloadFileListListener { + void onDownloadFileList(int status, @Nullable String message, @NonNull List userFiles); + } + + public interface OnCollectLocalFilesListener { + void onFileCollected(@NonNull GpxFileInfo fileInfo); + + void onFilesCollected(@NonNull List fileInfos); + } + + public interface OnGenerateBackupInfoListener { + void onBackupInfoGenerated(@Nullable BackupInfo backupInfo, @Nullable String error); + } + + public interface OnUploadFilesListener { + void onFileUploadProgress(@NonNull File file, int progress); + + void onFilesUploadDone(@NonNull Map errors); + } + + public interface OnDeleteFilesListener { + void onFileDeleteProgress(@NonNull UserFile file); + + void onFilesDeleteDone(@NonNull Map errors); + } + + public interface OnDownloadFileListener { + void onFileDownloadProgress(@NonNull UserFile userFile, int progress); + @WorkerThread + void onFileDownloadedAsync(@NonNull File file); + void onFilesDownloadDone(@NonNull Map errors); + } + + public static class BackupInfo { + public List filesToDownload = new ArrayList<>(); + public List filesToUpload = new ArrayList<>(); + public List filesToDelete = new ArrayList<>(); + public List> filesToMerge = new ArrayList<>(); + } + + public BackupHelper(@NonNull OsmandApplication app) { + this.app = app; + this.settings = app.getSettings(); + this.favouritesHelper = app.getFavorites(); + this.gpxHelper = app.getGpxDbHelper(); + } + + @SuppressLint("HardwareIds") + private String getAndroidId() { + try { + return Settings.Secure.getString(app.getContentResolver(), Settings.Secure.ANDROID_ID); + } catch (Exception e) { + return null; + } + } + + public static boolean isTokenValid(@NonNull String token) { + return token.matches("[0-9]+"); + } + + @Nullable + public String getOrderId() { + InAppPurchaseHelper purchaseHelper = app.getInAppPurchaseHelper(); + InAppSubscription purchasedSubscription = purchaseHelper.getAnyPurchasedSubscription(); + return purchasedSubscription != null ? purchasedSubscription.getOrderId() : null; + } + + public String getDeviceId() { + return settings.BACKUP_DEVICE_ID.get(); + } + + public String getAccessToken() { + return settings.BACKUP_ACCESS_TOKEN.get(); + } + + public String getEmail() { + return settings.BACKUP_USER_EMAIL.get(); + } + + public boolean isRegistered() { + return !Algorithms.isEmpty(getDeviceId()) && !Algorithms.isEmpty(getAccessToken()); + } + + private void checkRegistered() throws UserNotRegisteredException { + if (Algorithms.isEmpty(getDeviceId()) || Algorithms.isEmpty(getAccessToken())) { + throw new UserNotRegisteredException(); + } + } + + public void registerUser(@NonNull String email, @Nullable final OnRegisterUserListener listener) { + Map params = new HashMap<>(); + params.put("email", email); + String orderId = getOrderId(); + if (!Algorithms.isEmpty(orderId)) { + params.put("orderid", orderId); + } + params.put("deviceid", app.getUserAndroidId()); + AndroidNetworkUtils.sendRequestAsync(app, USER_REGISTER_URL, params, "Register user", true, true, new OnRequestResultListener() { + @Override + public void onResult(String resultJson) { + int status; + String message; + if (!Algorithms.isEmpty(resultJson)) { + try { + JSONObject result = new JSONObject(resultJson); + String statusStr = result.getString("status"); + if (statusStr.equals("ok")) { + message = "You have been registered successfully. Please check for email with activation code."; + status = STATUS_SUCCESS; + } else { + message = "User registration error: " + statusStr; + status = STATUS_SERVER_ERROR; + } + } catch (JSONException e) { + message = "User registration error: json parsing"; + status = STATUS_PARSE_JSON_ERROR; + } + } else { + message = "User registration error: empty response"; + status = STATUS_EMPTY_RESPONSE_ERROR; + } + if (listener != null) { + listener.onRegisterUser(status, message); + } + } + }); + } + + public void registerDevice(String token, @Nullable final OnRegisterDeviceListener listener) { + Map params = new HashMap<>(); + params.put("email", getEmail()); + String orderId = getOrderId(); + if (orderId != null) { + params.put("orderid", orderId); + } + String androidId = getAndroidId(); + if (!Algorithms.isEmpty(androidId)) { + params.put("deviceid", androidId); + } + params.put("token", token); + AndroidNetworkUtils.sendRequestAsync(app, DEVICE_REGISTER_URL, params, "Register device", true, true, new OnRequestResultListener() { + @Override + public void onResult(String resultJson) { + int status; + String message; + if (!Algorithms.isEmpty(resultJson)) { + try { + JSONObject result = new JSONObject(resultJson); + settings.BACKUP_DEVICE_ID.set(result.getString("id")); + settings.BACKUP_USER_ID.set(result.getString("userid")); + settings.BACKUP_NATIVE_DEVICE_ID.set(result.getString("deviceid")); + settings.BACKUP_ACCESS_TOKEN.set(result.getString("accesstoken")); + settings.BACKUP_ACCESS_TOKEN_UPDATE_TIME.set(result.getString("udpatetime")); + status = STATUS_SUCCESS; + message = "Device have been registered successfully"; + } catch (JSONException e) { + message = "Device registration error: json parsing"; + status = STATUS_PARSE_JSON_ERROR; + } + } else { + message = "Device registration error: empty response"; + status = STATUS_EMPTY_RESPONSE_ERROR; + } + if (listener != null) { + listener.onRegisterDevice(status, message); + } + } + }); + } + + public void uploadFiles(@NonNull List gpxFiles, @Nullable final OnUploadFilesListener listener) throws UserNotRegisteredException { + checkRegistered(); + + Map params = new HashMap<>(); + params.put("deviceid", getDeviceId()); + params.put("accessToken", getAccessToken()); + Map headers = new HashMap<>(); + headers.put("Accept-Encoding", "deflate, gzip"); + + final Map gpxInfos = new HashMap<>(); + for (GpxFileInfo gpxFile : gpxFiles) { + gpxInfos.put(gpxFile.file, gpxFile); + } + final File favoritesFile = favouritesHelper.getExternalFile(); + AndroidNetworkUtils.uploadFilesAsync(UPLOAD_FILE_URL, new ArrayList<>(gpxInfos.keySet()), true, params, headers, new OnFilesUploadCallback() { + @Nullable + @Override + public Map getAdditionalParams(@NonNull File file) { + Map additionaParams = new HashMap<>(); + GpxFileInfo gpxFileInfo = gpxInfos.get(file); + if (gpxFileInfo != null) { + additionaParams.put("name", gpxFileInfo.getFileName(true)); + additionaParams.put("type", Algorithms.getFileExtension(file)); + gpxFileInfo.uploadTime = System.currentTimeMillis(); + if (file.equals(favoritesFile)) { + favouritesHelper.setLastUploadedTime(gpxFileInfo.uploadTime); + } else { + GpxDataItem gpxItem = gpxHelper.getItem(file); + if (gpxItem != null) { + gpxHelper.updateLastUploadedTime(gpxItem, gpxFileInfo.uploadTime); + } + } + additionaParams.put("clienttime", String.valueOf(gpxFileInfo.uploadTime)); + } + return additionaParams; + } + + @Override + public void onFileUploadProgress(@NonNull File file, int progress) { + if (listener != null) { + listener.onFileUploadProgress(file, progress); + } + } + + @Override + public void onFilesUploadDone(@NonNull Map errors) { + if (errors.isEmpty()) { + settings.BACKUP_LAST_UPLOADED_TIME.set(System.currentTimeMillis() + 1); + } + if (listener != null) { + listener.onFilesUploadDone(errors); + } + } + }); + } + + public void deleteFiles(@NonNull List userFiles, @Nullable final OnDeleteFilesListener listener) throws UserNotRegisteredException { + checkRegistered(); + + Map commonParameters = new HashMap<>(); + commonParameters.put("deviceid", getDeviceId()); + commonParameters.put("accessToken", getAccessToken()); + + final List requests = new ArrayList<>(); + final Map filesMap = new HashMap<>(); + for (UserFile userFile : userFiles) { + Map parameters = new HashMap<>(commonParameters); + parameters.put("name", userFile.getName()); + parameters.put("type", userFile.getType()); + Request r = new Request(DELETE_FILE_URL, parameters, null, false, true); + requests.add(r); + filesMap.put(r, userFile); + } + AndroidNetworkUtils.sendRequestsAsync(null, requests, new OnSendRequestsListener() { + @Override + public void onRequestSent(@NonNull RequestResponse response) { + if (listener != null) { + UserFile userFile = filesMap.get(response.getRequest()); + if (userFile != null) { + listener.onFileDeleteProgress(userFile); + } + } + } + + @Override + public void onRequestsSent(@NonNull List results) { + if (listener != null) { + Map errors = new HashMap<>(); + for (RequestResponse response : results) { + UserFile userFile = filesMap.get(response.getRequest()); + if (userFile != null) { + String responseStr = response.getResponse(); + boolean success; + try { + JSONObject json = new JSONObject(responseStr); + String status = json.getString("status"); + success = status.equalsIgnoreCase("ok"); + } catch (JSONException e) { + success = false; + } + if (!success) { + errors.put(userFile, responseStr); + } + } + } + listener.onFilesDeleteDone(errors); + } + } + }); + } + + public void downloadFileList(@Nullable final OnDownloadFileListListener listener) throws UserNotRegisteredException { + checkRegistered(); + + Map params = new HashMap<>(); + params.put("deviceid", getDeviceId()); + params.put("accessToken", getAccessToken()); + AndroidNetworkUtils.sendRequestAsync(app, LIST_FILES_URL, params, "Download file list", true, false, new OnRequestResultListener() { + @Override + public void onResult(String resultJson) { + int status; + String message; + List userFiles = new ArrayList<>(); + if (!Algorithms.isEmpty(resultJson)) { + try { + JSONObject result = new JSONObject(resultJson); + String totalZipSize = result.getString("totalZipSize"); + String totalFiles = result.getString("totalFiles"); + String totalFileVersions = result.getString("totalFileVersions"); + JSONArray files = result.getJSONArray("uniqueFiles"); + for (int i = 0; i < files.length(); i++) { + userFiles.add(new UserFile(files.getJSONObject(i))); + } + + status = STATUS_SUCCESS; + message = "Total files: " + totalFiles + "\n" + + "Total zip size: " + AndroidUtils.formatSize(app, Long.parseLong(totalZipSize)) + "\n" + + "Total file versions: " + totalFileVersions; + } catch (JSONException | ParseException e) { + status = STATUS_PARSE_JSON_ERROR; + message = "Download file list error: json parsing"; + } + } else { + status = STATUS_EMPTY_RESPONSE_ERROR; + message = "Download file list error: empty response"; + } + if (listener != null) { + listener.onDownloadFileList(status, message, userFiles); + } + } + }); + } + + public void downloadFiles(@NonNull final Map filesMap, @Nullable final OnDownloadFileListener listener) throws UserNotRegisteredException { + checkRegistered(); + + Map params = new HashMap<>(); + params.put("deviceid", getDeviceId()); + params.put("accessToken", getAccessToken()); + AndroidNetworkUtils.downloadFilesAsync(DOWNLOAD_FILE_URL, + new ArrayList<>(filesMap.keySet()), params, new OnFilesDownloadCallback() { + @Nullable + @Override + public Map getAdditionalParams(@NonNull File file) { + UserFile userFile = filesMap.get(file); + Map additionaParams = new HashMap<>(); + additionaParams.put("name", userFile.getName()); + additionaParams.put("type", userFile.getType()); + return additionaParams; + } + + @Override + public void onFileDownloadProgress(@NonNull File file, int percent) { + if (listener != null) { + listener.onFileDownloadProgress(filesMap.get(file), percent); + } + } + + @Override + public void onFileDownloadedAsync(@NonNull File file) { + if (listener != null) { + listener.onFileDownloadedAsync(file); + } + } + + @Override + public void onFilesDownloadDone(@NonNull Map errors) { + if (listener != null) { + listener.onFilesDownloadDone(errors); + } + } + }); + } + + @SuppressLint("StaticFieldLeak") + public void collectLocalFiles(@Nullable final OnCollectLocalFilesListener listener) { + AsyncTask> task = new AsyncTask>() { + + private final OnCollectLocalFilesListener internalListener = new OnCollectLocalFilesListener() { + @Override + public void onFileCollected(@NonNull GpxFileInfo fileInfo) { + publishProgress(fileInfo); + } + + @Override + public void onFilesCollected(@NonNull List fileInfos) { + } + }; + + private void loadGPXData(@NonNull File mapPath, @NonNull List result, + @Nullable OnCollectLocalFilesListener listener) { + if (mapPath.canRead()) { + loadGPXFolder(mapPath, result, "", listener); + } + } + + private void loadGPXFolder(@NonNull File mapPath, @NonNull List result, + @NonNull String gpxSubfolder, @Nullable OnCollectLocalFilesListener listener) { + File[] listFiles = mapPath.listFiles(); + if (listFiles != null) { + for (File gpxFile : listFiles) { + if (gpxFile.isDirectory()) { + String sub = gpxSubfolder.length() == 0 ? gpxFile.getName() : gpxSubfolder + "/" + + gpxFile.getName(); + loadGPXFolder(gpxFile, result, sub, listener); + } else if (gpxFile.isFile() && gpxFile.getName().toLowerCase().endsWith(IndexConstants.GPX_FILE_EXT)) { + GpxFileInfo info = new GpxFileInfo(); + info.subfolder = gpxSubfolder; + info.file = gpxFile; + GpxDataItem gpxItem = gpxHelper.getItem(gpxFile); + if (gpxItem != null) { + info.uploadTime = gpxItem.getFileLastUploadedTime(); + } + result.add(info); + if (listener != null) { + listener.onFileCollected(info); + } + } + } + } + } + + @Override + protected List doInBackground(Void... voids) { + List result = new ArrayList<>(); + + GpxFileInfo favInfo = new GpxFileInfo(); + favInfo.subfolder = ""; + favInfo.file = favouritesHelper.getExternalFile(); + favInfo.uploadTime = favouritesHelper.getLastUploadedTime(); + result.add(favInfo); + if (listener != null) { + listener.onFileCollected(favInfo); + } + + loadGPXData(app.getAppPath(IndexConstants.GPX_INDEX_DIR), result, internalListener); + return result; + } + + @Override + protected void onProgressUpdate(GpxFileInfo... fileInfos) { + if (listener != null) { + listener.onFileCollected(fileInfos[0]); + } + } + + @Override + protected void onPostExecute(List fileInfos) { + if (listener != null) { + listener.onFilesCollected(fileInfos); + } + } + }; + task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + + @SuppressLint("StaticFieldLeak") + public void generateBackupInfo(@NonNull final List localFiles, @NonNull final List remoteFiles, + @Nullable final OnGenerateBackupInfoListener listener) { + + final long backupLastUploadedTime = settings.BACKUP_LAST_UPLOADED_TIME.get(); + + AsyncTask task = new AsyncTask() { + @Override + protected BackupInfo doInBackground(Void... voids) { + BackupInfo info = new BackupInfo(); + for (UserFile remoteFile : remoteFiles) { + boolean hasLocalFile = false; + for (GpxFileInfo localFile : localFiles) { + if (remoteFile.getName().equals(localFile.getFileName(true))) { + hasLocalFile = true; + long remoteUploadTime = remoteFile.getClienttimems(); + long localUploadTime = localFile.uploadTime; + long localModifiedTime = localFile.file.lastModified(); + if (remoteUploadTime == localUploadTime) { + if (localUploadTime < localModifiedTime) { + info.filesToUpload.add(localFile); + } + } else { + info.filesToMerge.add(new Pair<>(localFile, remoteFile)); + } + break; + } + } + if (!hasLocalFile) { + if (backupLastUploadedTime > 0 && backupLastUploadedTime >= remoteFile.getClienttimems()) { + info.filesToDelete.add(remoteFile); + } else { + info.filesToDownload.add(remoteFile); + } + } + } + for (GpxFileInfo localFile : localFiles) { + boolean hasRemoteFile = false; + for (UserFile remoteFile : remoteFiles) { + if (localFile.getFileName(true).equals(remoteFile.getName())) { + hasRemoteFile = true; + break; + } + } + if (!hasRemoteFile) { + info.filesToUpload.add(localFile); + } + } + return info; + } + + @Override + protected void onPostExecute(BackupInfo backupInfo) { + if (listener != null) { + listener.onBackupInfoGenerated(backupInfo, null); + } + } + }; + task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } +} diff --git a/OsmAnd/src/net/osmand/plus/backup/BackupTask.java b/OsmAnd/src/net/osmand/plus/backup/BackupTask.java new file mode 100644 index 0000000000..a2fc299463 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/backup/BackupTask.java @@ -0,0 +1,335 @@ +package net.osmand.plus.backup; + +import android.app.Activity; +import android.app.ProgressDialog; +import android.content.Context; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import net.osmand.AndroidUtils; +import net.osmand.GPXUtilities; +import net.osmand.GPXUtilities.GPXFile; +import net.osmand.plus.GPXDatabase.GpxDataItem; +import net.osmand.plus.GpxDbHelper; +import net.osmand.plus.OsmandApplication; +import net.osmand.plus.ProgressImplementation; +import net.osmand.plus.backup.BackupHelper.BackupInfo; +import net.osmand.plus.backup.BackupHelper.OnDeleteFilesListener; +import net.osmand.plus.backup.BackupHelper.OnDownloadFileListener; +import net.osmand.plus.backup.BackupHelper.OnUploadFilesListener; +import net.osmand.plus.importfiles.FavoritesImportTask; +import net.osmand.util.Algorithms; + +import java.io.File; +import java.lang.ref.WeakReference; +import java.util.HashMap; +import java.util.Map; +import java.util.Stack; + +import static net.osmand.IndexConstants.GPX_INDEX_DIR; +import static net.osmand.IndexConstants.TEMP_DIR; + +public class BackupTask { + + private final OsmandApplication app; + private final BackupHelper backupHelper; + + private final OnBackupListener listener; + private final WeakReference contextRef; + private ProgressImplementation progress; + + private final BackupInfo backupInfo; + private Map uploadErrors; + private Map downloadErrors; + private Map deleteErrors; + private String error; + + private final TaskType[] backupTasks = {TaskType.UPLOAD_FILES, TaskType.DELETE_FILES}; + private final TaskType[] restoreTasks = {TaskType.DOWNLOAD_FILES}; + + private Stack runningTasks = new Stack<>(); + + private enum TaskType { + UPLOAD_FILES, + DOWNLOAD_FILES, + DELETE_FILES + } + + public interface OnBackupListener { + void onBackupDone(@Nullable Map uploadErrors, + @Nullable Map downloadErrors, + @Nullable Map deleteErrors, @Nullable String error); + } + + public BackupTask(@NonNull BackupInfo backupInfo, @NonNull Context context, @Nullable OnBackupListener listener) { + this.contextRef = new WeakReference<>(context); + this.app = (OsmandApplication) context.getApplicationContext(); + this.backupHelper = app.getBackupHelper(); + this.backupInfo = backupInfo; + this.listener = listener; + } + + public BackupInfo getBackupInfo() { + return backupInfo; + } + + public Map getUploadErrors() { + return uploadErrors; + } + + public Map getDownloadErrors() { + return downloadErrors; + } + + public Map getDeleteErrors() { + return deleteErrors; + } + + public String getError() { + return error; + } + + public boolean runBackup() { + if (!runningTasks.empty()) { + return false; + } + initBackupTasks(); + return runTasks(); + } + + public boolean runRestore() { + if (!runningTasks.empty()) { + return false; + } + initRestoreTasks(); + return runTasks(); + } + + private void initBackupTasks() { + initData(); + Stack tasks = new Stack<>(); + for (int i = backupTasks.length - 1; i >= 0; i--) { + tasks.push(backupTasks[i]); + } + this.runningTasks = tasks; + onTasksInit(); + } + + private void initRestoreTasks() { + initData(); + Stack tasks = new Stack<>(); + for (int i = restoreTasks.length - 1; i >= 0; i--) { + tasks.push(restoreTasks[i]); + } + this.runningTasks = tasks; + onTasksInit(); + } + + private void initData() { + uploadErrors = null; + downloadErrors = null; + deleteErrors = null; + error = null; + } + + private boolean runTasks() { + if (runningTasks.empty()) { + return false; + } else { + TaskType taskType = runningTasks.pop(); + runTask(taskType); + return true; + } + } + + private void runTask(@NonNull TaskType taskType) { + switch (taskType) { + case UPLOAD_FILES: + doUploadFiles(); + break; + case DOWNLOAD_FILES: + doDownloadFiles(); + break; + case DELETE_FILES: + doDeleteFiles(); + break; + } + } + + private void onTaskFinished(@NonNull TaskType taskType) { + if (!runTasks()) { + onTasksDone(); + } + } + + private void doUploadFiles() { + if (Algorithms.isEmpty(backupInfo.filesToUpload)) { + onTaskFinished(TaskType.UPLOAD_FILES); + return; + } + onTaskProgressUpdate("Upload files..."); + try { + backupHelper.uploadFiles(backupInfo.filesToUpload, new OnUploadFilesListener() { + @Override + public void onFileUploadProgress(@NonNull File file, int progress) { + if (progress == 0) { + onTaskProgressUpdate(file.getName(), (int) (file.length() / 1024)); + } else { + onTaskProgressUpdate(progress); + } + } + + @Override + public void onFilesUploadDone(@NonNull Map errors) { + uploadErrors = errors; + onTaskFinished(TaskType.UPLOAD_FILES); + } + }); + } catch (UserNotRegisteredException e) { + onError("User is not registered"); + } + } + + private void doDownloadFiles() { + if (Algorithms.isEmpty(backupInfo.filesToDownload)) { + onTaskFinished(TaskType.DOWNLOAD_FILES); + return; + } + onTaskProgressUpdate("Download files..."); + File favoritesFile = app.getFavorites().getExternalFile(); + String favoritesFileName = favoritesFile.getName(); + File tempFavoritesFile = null; + final Map filesMap = new HashMap<>(); + for (UserFile userFile : backupInfo.filesToDownload) { + File file; + String fileName = userFile.getName(); + if (favoritesFileName.equals(fileName)) { + file = new File(app.getAppPath(TEMP_DIR), fileName); + tempFavoritesFile = file; + } else { + file = new File(app.getAppPath(GPX_INDEX_DIR), fileName); + } + filesMap.put(file, userFile); + } + final File finalTempFavoritesFile = tempFavoritesFile; + try { + backupHelper.downloadFiles(filesMap, new OnDownloadFileListener() { + @Override + public void onFileDownloadProgress(@NonNull UserFile userFile, int progress) { + if (progress == 0) { + onTaskProgressUpdate(new File(userFile.getName()).getName(), userFile.getFilesize() / 1024); + } else { + onTaskProgressUpdate(progress); + } + } + + @Override + public void onFileDownloadedAsync(@NonNull File file) { + UserFile userFile = filesMap.get(file); + long userFileTime = userFile.getClienttimems(); + if (file.equals(finalTempFavoritesFile)) { + GPXFile gpxFile = GPXUtilities.loadGPXFile(finalTempFavoritesFile); + FavoritesImportTask.mergeFavorites(app, gpxFile, "", false); + finalTempFavoritesFile.delete(); + app.getFavorites().getExternalFile().setLastModified(userFileTime); + } else { + file.setLastModified(userFileTime); + GpxDataItem item = new GpxDataItem(file, userFileTime); + app.getGpxDbHelper().add(item); + } + } + + @Override + public void onFilesDownloadDone(@NonNull Map errors) { + downloadErrors = errors; + onTaskFinished(TaskType.DOWNLOAD_FILES); + } + }); + } catch (UserNotRegisteredException e) { + onError("User is not registered"); + } + } + + private void doDeleteFiles() { + if (Algorithms.isEmpty(backupInfo.filesToDelete)) { + onTaskFinished(TaskType.DELETE_FILES); + return; + } + onTaskProgressUpdate("Delete files..."); + try { + backupHelper.deleteFiles(backupInfo.filesToDelete, new OnDeleteFilesListener() { + @Override + public void onFileDeleteProgress(@NonNull UserFile userFile) { + onTaskProgressUpdate(userFile.getName()); + } + + @Override + public void onFilesDeleteDone(@NonNull Map errors) { + deleteErrors = errors; + onTaskFinished(TaskType.DELETE_FILES); + } + }); + } catch (UserNotRegisteredException e) { + onError("User is not registered"); + } + } + + private void onTasksInit() { + Context ctx = contextRef.get(); + if (ctx instanceof Activity && AndroidUtils.isActivityNotDestroyed((Activity) ctx) && progress != null) { + progress = ProgressImplementation.createProgressDialog(ctx, + "Backup data", "Initializing...", ProgressDialog.STYLE_HORIZONTAL); + } + } + + private void onTaskProgressUpdate(Object... objects) { + Context ctx = contextRef.get(); + if (ctx instanceof Activity && AndroidUtils.isActivityNotDestroyed((Activity) ctx) && progress != null) { + if (objects != null) { + if (objects.length == 1) { + if (objects[0] instanceof String) { + progress.startTask((String) objects[0], -1); + } else if (objects[0] instanceof Integer) { + int progressValue = (Integer) objects[0]; + if (progressValue < Integer.MAX_VALUE) { + progress.progress(progressValue); + } else { + progress.finishTask(); + } + } + } else if (objects.length == 2) { + progress.startTask((String) objects[0], (Integer) objects[1]); + } + } + } + } + + private void onError(@NonNull String message) { + this.error = message; + runningTasks.clear(); + onTasksDone(); + } + + private void onTasksDone() { + if (listener != null) { + listener.onBackupDone(uploadErrors, downloadErrors, deleteErrors, error); + } + Context ctx = contextRef.get(); + if (ctx instanceof Activity && AndroidUtils.isActivityNotDestroyed((Activity) ctx) && progress != null) { + progress.finishTask(); + app.runInUIThread(new Runnable() { + @Override + public void run() { + try { + if (progress.getDialog().isShowing()) { + progress.getDialog().dismiss(); + } + } catch (Exception e) { + //ignored + } + } + }, 300); + } + } +} diff --git a/OsmAnd/src/net/osmand/plus/backup/GpxFileInfo.java b/OsmAnd/src/net/osmand/plus/backup/GpxFileInfo.java new file mode 100644 index 0000000000..228e119fd6 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/backup/GpxFileInfo.java @@ -0,0 +1,65 @@ +package net.osmand.plus.backup; + +import net.osmand.util.Algorithms; + +import java.io.File; + +public class GpxFileInfo { + public File file; + public String subfolder; + public long uploadTime = 0; + + private String name = null; + private int sz = -1; + private String fileName = null; + + public String getName() { + if (name == null) { + name = formatName(file.getName()); + } + return name; + } + + private String formatName(String name) { + int ext = name.lastIndexOf('.'); + if (ext != -1) { + name = name.substring(0, ext); + } + return name.replace('_', ' '); + } + + // Usage: AndroidUtils.formatSize(v.getContext(), getSize() * 1024l); + public int getSize() { + if (sz == -1) { + if (file == null) { + return -1; + } + sz = (int) ((file.length() + 512) >> 10); + } + return sz; + } + + public long getFileDate() { + if (file == null) { + return 0; + } + return file.lastModified(); + } + + public String getFileName(boolean includeSubfolder) { + String result; + if (fileName != null) { + result = fileName; + } else { + if (file == null) { + result = ""; + } else { + result = fileName = file.getName(); + } + } + if (includeSubfolder && !Algorithms.isEmpty(subfolder)) { + result = subfolder + "/" + result; + } + return result; + } +} diff --git a/OsmAnd/src/net/osmand/plus/backup/PrepareBackupTask.java b/OsmAnd/src/net/osmand/plus/backup/PrepareBackupTask.java new file mode 100644 index 0000000000..d3932b5ba8 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/backup/PrepareBackupTask.java @@ -0,0 +1,211 @@ +package net.osmand.plus.backup; + +import android.app.Activity; +import android.app.ProgressDialog; +import android.content.Context; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import net.osmand.AndroidUtils; +import net.osmand.plus.OsmandApplication; +import net.osmand.plus.ProgressImplementation; +import net.osmand.plus.backup.BackupHelper.BackupInfo; +import net.osmand.plus.backup.BackupHelper.OnCollectLocalFilesListener; +import net.osmand.plus.backup.BackupHelper.OnDownloadFileListListener; +import net.osmand.plus.backup.BackupHelper.OnGenerateBackupInfoListener; +import net.osmand.util.Algorithms; + +import java.lang.ref.WeakReference; +import java.util.List; +import java.util.Stack; + +public class PrepareBackupTask { + + private final OsmandApplication app; + private final BackupHelper backupHelper; + + private final OnPrepareBackupListener listener; + private final WeakReference contextRef; + private ProgressImplementation progress; + + private BackupInfo result; + private List userFiles; + private List fileInfos; + private String error; + + private Stack runningTasks = new Stack<>(); + + private enum TaskType { + COLLECT_LOCAL_FILES, + COLLECT_REMOTE_FILES, + GENERATE_BACKUP_INFO + } + + public interface OnPrepareBackupListener { + void onBackupPrepared(@Nullable BackupInfo backupInfo, @Nullable String error); + } + + public PrepareBackupTask(@NonNull Context context, @Nullable OnPrepareBackupListener listener) { + this.contextRef = new WeakReference<>(context); + this.app = (OsmandApplication) context.getApplicationContext(); + this.backupHelper = app.getBackupHelper(); + this.listener = listener; + } + + public BackupInfo getResult() { + return result; + } + + public String getError() { + return error; + } + + public boolean prepare() { + if (!runningTasks.empty()) { + return false; + } + initTasks(); + return runTasks(); + } + + private void initTasks() { + result = null; + userFiles = null; + fileInfos = null; + error = null; + Stack tasks = new Stack<>(); + TaskType[] types = TaskType.values(); + for (int i = types.length - 1; i >= 0; i--) { + tasks.push(types[i]); + } + this.runningTasks = tasks; + onTasksInit(); + } + + private boolean runTasks() { + if (runningTasks.empty()) { + return false; + } else { + TaskType taskType = runningTasks.pop(); + runTask(taskType); + return true; + } + } + + private void runTask(@NonNull TaskType taskType) { + switch (taskType) { + case COLLECT_LOCAL_FILES: + doCollectLocalFiles(); + break; + case COLLECT_REMOTE_FILES: + doCollectRemoteFiles(); + break; + case GENERATE_BACKUP_INFO: + doGenerateBackupInfo(); + break; + } + } + + private void onTaskFinished(@NonNull TaskType taskType) { + if (!runTasks()) { + onTasksDone(); + } + } + + private void doCollectLocalFiles() { + onTaskProgressUpdate("Collecting local info..."); + backupHelper.collectLocalFiles(new OnCollectLocalFilesListener() { + @Override + public void onFileCollected(@NonNull GpxFileInfo fileInfo) { + } + + @Override + public void onFilesCollected(@NonNull List fileInfos) { + PrepareBackupTask.this.fileInfos = fileInfos; + onTaskFinished(TaskType.COLLECT_LOCAL_FILES); + } + }); + } + + private void doCollectRemoteFiles() { + onTaskProgressUpdate("Downloading remote info..."); + try { + backupHelper.downloadFileList(new OnDownloadFileListListener() { + @Override + public void onDownloadFileList(int status, @Nullable String message, @NonNull List userFiles) { + if (status == BackupHelper.STATUS_SUCCESS) { + PrepareBackupTask.this.userFiles = userFiles; + } else { + onError(!Algorithms.isEmpty(message) ? message : "Download file list error: " + status); + } + onTaskFinished(TaskType.COLLECT_REMOTE_FILES); + } + }); + } catch (UserNotRegisteredException e) { + onError("User is not registered"); + } + } + + private void doGenerateBackupInfo() { + if (fileInfos == null || userFiles == null) { + onTaskFinished(TaskType.GENERATE_BACKUP_INFO); + return; + } + onTaskProgressUpdate("Generating backup info..."); + backupHelper.generateBackupInfo(fileInfos, userFiles, new OnGenerateBackupInfoListener() { + @Override + public void onBackupInfoGenerated(@Nullable BackupInfo backupInfo, @Nullable String error) { + if (Algorithms.isEmpty(error)) { + PrepareBackupTask.this.result = backupInfo; + } else { + onError(error); + } + onTaskFinished(TaskType.GENERATE_BACKUP_INFO); + } + }); + } + + private void onTasksInit() { + Context ctx = contextRef.get(); + if (ctx instanceof Activity && AndroidUtils.isActivityNotDestroyed((Activity) ctx)) { + progress = ProgressImplementation.createProgressDialog(ctx, + "Prepare backup", "Initializing...", ProgressDialog.STYLE_HORIZONTAL); + } + } + + private void onTaskProgressUpdate(String message) { + Context ctx = contextRef.get(); + if (ctx instanceof Activity && AndroidUtils.isActivityNotDestroyed((Activity) ctx) && progress != null) { + progress.startTask(message, -1); + } + } + + private void onError(@NonNull String message) { + this.error = message; + runningTasks.clear(); + onTasksDone(); + } + + private void onTasksDone() { + if (listener != null) { + listener.onBackupPrepared(result, error); + } + Context ctx = contextRef.get(); + if (ctx instanceof Activity && AndroidUtils.isActivityNotDestroyed((Activity) ctx) && progress != null) { + progress.finishTask(); + app.runInUIThread(new Runnable() { + @Override + public void run() { + try { + if (progress.getDialog().isShowing()) { + progress.getDialog().dismiss(); + } + } catch (Exception e) { + //ignored + } + } + }, 300); + } + } +} diff --git a/OsmAnd/src/net/osmand/plus/backup/UserFile.java b/OsmAnd/src/net/osmand/plus/backup/UserFile.java new file mode 100644 index 0000000000..437c46963b --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/backup/UserFile.java @@ -0,0 +1,102 @@ +package net.osmand.plus.backup; + +import androidx.annotation.NonNull; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.text.DateFormat; +import java.text.ParseException; +import java.util.Date; +import java.util.Locale; + +public class UserFile { + + private int userid; + private long id; + private int deviceid; + private int filesize; + private String type; + private String name; + private Date updatetime; + private long updatetimems; + private Date clienttime; + private long clienttimems; + private int zipSize; + + public UserFile(@NonNull JSONObject json) throws JSONException, ParseException { + if (json.has("userid")) { + userid = json.getInt("userid"); + } + if (json.has("id")) { + id = json.getLong("id"); + } + if (json.has("deviceid")) { + deviceid = json.getInt("deviceid"); + } + if (json.has("filesize")) { + filesize = json.getInt("filesize"); + } + if (json.has("type")) { + type = json.getString("type"); + } + if (json.has("name")) { + name = json.getString("name"); + } + if (json.has("updatetimems")) { + updatetimems = json.getLong("updatetimems"); + updatetime = new Date(updatetimems); + } + if (json.has("clienttimems")) { + clienttimems = json.getLong("clienttimems"); + clienttime = new Date(clienttimems); + } + if (json.has("zipSize")) { + zipSize = json.getInt("zipSize"); + } + } + + public int getUserid() { + return userid; + } + + public long getId() { + return id; + } + + public int getDeviceid() { + return deviceid; + } + + public int getFilesize() { + return filesize; + } + + public String getType() { + return type; + } + + public String getName() { + return name; + } + + public Date getUpdatetime() { + return updatetime; + } + + public long getUpdatetimems() { + return updatetimems; + } + + public Date getClienttime() { + return clienttime; + } + + public long getClienttimems() { + return clienttimems; + } + + public int getZipSize() { + return zipSize; + } +} diff --git a/OsmAnd/src/net/osmand/plus/backup/UserNotRegisteredException.java b/OsmAnd/src/net/osmand/plus/backup/UserNotRegisteredException.java new file mode 100644 index 0000000000..a414022b91 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/backup/UserNotRegisteredException.java @@ -0,0 +1,9 @@ +package net.osmand.plus.backup; + +public class UserNotRegisteredException extends Exception { + private static final long serialVersionUID = -8005954380280822845L; + + public UserNotRegisteredException() { + super("User is not resistered"); + } +} diff --git a/OsmAnd/src/net/osmand/plus/base/ModeSelectionBottomSheet.java b/OsmAnd/src/net/osmand/plus/base/ModeSelectionBottomSheet.java new file mode 100644 index 0000000000..a19e304299 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/base/ModeSelectionBottomSheet.java @@ -0,0 +1,80 @@ +package net.osmand.plus.base; + +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.view.View; +import android.widget.ImageView; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.content.ContextCompat; +import androidx.fragment.app.FragmentManager; + +import net.osmand.AndroidUtils; +import net.osmand.plus.R; +import net.osmand.plus.widgets.multistatetoggle.RadioItem; + +import java.util.Collections; +import java.util.List; + +public class ModeSelectionBottomSheet extends SelectionBottomSheet { + + public static final String TAG = ModeSelectionBottomSheet.class.getSimpleName(); + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + showElements(primaryDescription, toggleContainer); + hideElements(checkBox, checkBoxTitle, titleDescription, + secondaryDescription, selectedSize, selectAllButton); + } + + @Override + protected void updateItemView(SelectableItem item, View view) { + ImageView ivIcon = view.findViewById(R.id.icon); + TextView tvTitle = view.findViewById(R.id.title); + TextView tvDescr = view.findViewById(R.id.description); + + Drawable icon = uiUtilities.getIcon(item.getIconId(), activeColorRes); + ivIcon.setImageDrawable(icon); + tvTitle.setText(item.getTitle()); + tvDescr.setText(item.getDescription()); + tvDescr.setTextColor(ContextCompat.getColor(app, AndroidUtils.getSecondaryTextColorId(nightMode))); + } + + @Override + protected int getItemLayoutId() { + return R.layout.bottom_sheet_item_with_descr_56dp; + } + + public void setItem(SelectableItem item) { + setItems(Collections.singletonList(item)); + } + + @NonNull + @Override + public List getSelectedItems() { + return allItems; + } + + @Override + protected boolean shouldShowDivider() { + return false; + } + + public static ModeSelectionBottomSheet showInstance(@NonNull AppCompatActivity activity, + @NonNull SelectableItem previewItem, + @NonNull List radioItems, + boolean usedOnMap) { + ModeSelectionBottomSheet fragment = new ModeSelectionBottomSheet(); + fragment.setUsedOnMap(usedOnMap); + fragment.setModes(radioItems); + fragment.setItems(Collections.singletonList(previewItem)); + FragmentManager fm = activity.getSupportFragmentManager(); + fragment.show(fm, TAG); + return fragment; + } + +} diff --git a/OsmAnd/src/net/osmand/plus/base/MultipleSelectionBottomSheet.java b/OsmAnd/src/net/osmand/plus/base/MultipleSelectionBottomSheet.java new file mode 100644 index 0000000000..35481944f2 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/base/MultipleSelectionBottomSheet.java @@ -0,0 +1,180 @@ +package net.osmand.plus.base; + +import android.content.res.ColorStateList; +import android.os.Bundle; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.CheckBox; +import android.widget.ImageView; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.content.ContextCompat; +import androidx.core.widget.CompoundButtonCompat; +import androidx.fragment.app.FragmentManager; + +import net.osmand.AndroidUtils; +import net.osmand.plus.R; +import net.osmand.plus.helpers.AndroidUiHelper; +import net.osmand.util.Algorithms; + +import java.util.ArrayList; +import java.util.List; + +import static net.osmand.view.ThreeStateCheckbox.State.CHECKED; +import static net.osmand.view.ThreeStateCheckbox.State.MISC; +import static net.osmand.view.ThreeStateCheckbox.State.UNCHECKED; + +public class MultipleSelectionBottomSheet extends SelectionBottomSheet { + + public static final String TAG = MultipleSelectionBottomSheet.class.getSimpleName(); + + private final List selectedItems = new ArrayList<>(); + private SelectionUpdateListener selectionUpdateListener; + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + selectAllButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + checkBox.performClick(); + boolean checked = checkBox.getState() == CHECKED; + if (checked) { + selectedItems.addAll(allItems); + } else { + selectedItems.clear(); + } + onSelectedItemsChanged(); + updateItemsSelection(checked); + } + }); + } + + @Override + protected boolean shouldShowDivider() { + return true; + } + + @Override + protected void updateItemView(final SelectableItem item, View view) { + boolean checked = selectedItems.contains(item); + ImageView imageView = view.findViewById(R.id.icon); + TextView title = view.findViewById(R.id.title); + TextView description = view.findViewById(R.id.description); + final CheckBox checkBox = view.findViewById(R.id.compound_button); + AndroidUiHelper.setVisibility(View.VISIBLE, imageView, title, description, checkBox); + + checkBox.setChecked(checked); + CompoundButtonCompat.setButtonTintList(checkBox, AndroidUtils.createCheckedColorStateList(app, secondaryColorRes, activeColorRes)); + + view.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + boolean checked = !checkBox.isChecked(); + checkBox.setChecked(checked); + if (checked) { + selectedItems.add(item); + } else { + selectedItems.remove(item); + } + onSelectedItemsChanged(); + } + }); + title.setText(item.getTitle()); + description.setText(item.getDescription()); + imageView.setImageDrawable(uiUtilities.getIcon(item.getIconId(), activeColorRes)); + } + + @Override + protected int getItemLayoutId() { + return R.layout.bottom_sheet_item_with_descr_and_checkbox_56dp; + } + + @Override + protected void notifyUiCreated() { + onSelectedItemsChanged(); + super.notifyUiCreated(); + } + + private void onSelectedItemsChanged() { + updateSelectAllButton(); + updateSelectedSizeView(); + updateApplyButtonEnable(); + if (selectionUpdateListener != null) { + selectionUpdateListener.onSelectionUpdate(); + } + } + + private void updateSelectAllButton() { + String checkBoxTitle; + if (Algorithms.isEmpty(selectedItems)) { + checkBox.setState(UNCHECKED); + checkBoxTitle = getString(R.string.shared_string_select_all); + } else { + checkBox.setState(selectedItems.containsAll(allItems) ? CHECKED : MISC); + checkBoxTitle = getString(R.string.shared_string_deselect_all); + } + int checkBoxColor = checkBox.getState() == UNCHECKED ? secondaryColorRes : activeColorRes; + CompoundButtonCompat.setButtonTintList(checkBox, ColorStateList.valueOf(ContextCompat.getColor(app, checkBoxColor))); + this.checkBoxTitle.setText(checkBoxTitle); + } + + private void updateSelectedSizeView() { + String selected = String.valueOf(selectedItems.size()); + String all = String.valueOf(allItems.size()); + selectedSize.setText(getString(R.string.ltr_or_rtl_combine_via_slash, selected, all)); + } + + private void updateApplyButtonEnable() { + boolean noEmptySelection = !Algorithms.isEmpty(selectedItems); + rightButton.setEnabled(noEmptySelection); + } + + private void updateItemsSelection(boolean checked) { + for (SelectableItem item : allItems) { + View v = listViews.get(item); + CheckBox checkBox = v != null ? (CheckBox) v.findViewById(R.id.compound_button) : null; + if (checkBox != null) { + checkBox.setChecked(checked); + } + } + } + + protected void setSelectedItems(List selected) { + selectedItems.clear(); + if (!Algorithms.isEmpty(selected)) { + selectedItems.addAll(selected); + } + } + + @NonNull + @Override + public List getSelectedItems() { + return selectedItems; + } + + public void setSelectionUpdateListener(SelectionUpdateListener selectionUpdateListener) { + this.selectionUpdateListener = selectionUpdateListener; + } + + public static MultipleSelectionBottomSheet showInstance(@NonNull AppCompatActivity activity, + @NonNull List items, + @Nullable List selected, + boolean usedOnMap) { + MultipleSelectionBottomSheet fragment = new MultipleSelectionBottomSheet(); + fragment.setUsedOnMap(usedOnMap); + fragment.setItems(items); + fragment.setSelectedItems(selected); + FragmentManager fm = activity.getSupportFragmentManager(); + fragment.show(fm, TAG); + return fragment; + } + + public interface SelectionUpdateListener { + void onSelectionUpdate(); + } + +} \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/base/MultipleSelectionWithModeBottomSheet.java b/OsmAnd/src/net/osmand/plus/base/MultipleSelectionWithModeBottomSheet.java new file mode 100644 index 0000000000..9ac4948937 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/base/MultipleSelectionWithModeBottomSheet.java @@ -0,0 +1,41 @@ +package net.osmand.plus.base; + +import android.os.Bundle; +import android.view.View; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; +import androidx.fragment.app.FragmentManager; + +import net.osmand.plus.widgets.multistatetoggle.RadioItem; + +import java.util.List; + +public class MultipleSelectionWithModeBottomSheet extends MultipleSelectionBottomSheet { + + public static final String TAG = MultipleSelectionWithModeBottomSheet.class.getSimpleName(); + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + showElements(secondaryDescription, toggleContainer, checkBox, + checkBoxTitle, titleDescription, selectedSize, selectAllButton); + } + + public static MultipleSelectionWithModeBottomSheet showInstance(@NonNull AppCompatActivity activity, + @NonNull List items, + @Nullable List selected, + @NonNull List modes, + boolean usedOnMap) { + MultipleSelectionWithModeBottomSheet fragment = new MultipleSelectionWithModeBottomSheet(); + fragment.setUsedOnMap(usedOnMap); + fragment.setItems(items); + fragment.setSelectedItems(selected); + fragment.setModes(modes); + FragmentManager fm = activity.getSupportFragmentManager(); + fragment.show(fm, TAG); + return fragment; + } + +} diff --git a/OsmAnd/src/net/osmand/plus/base/SelectMultipleItemsBottomSheet.java b/OsmAnd/src/net/osmand/plus/base/SelectMultipleItemsBottomSheet.java deleted file mode 100644 index 60b507c972..0000000000 --- a/OsmAnd/src/net/osmand/plus/base/SelectMultipleItemsBottomSheet.java +++ /dev/null @@ -1,301 +0,0 @@ -package net.osmand.plus.base; - -import android.content.res.ColorStateList; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.app.AppCompatActivity; -import androidx.core.content.ContextCompat; -import androidx.core.widget.CompoundButtonCompat; -import androidx.fragment.app.FragmentManager; - -import net.osmand.AndroidUtils; -import net.osmand.plus.OsmandApplication; -import net.osmand.plus.R; -import net.osmand.plus.UiUtilities; -import net.osmand.plus.base.bottomsheetmenu.BaseBottomSheetItem; -import net.osmand.plus.base.bottomsheetmenu.BottomSheetItemWithCompoundButton; -import net.osmand.plus.base.bottomsheetmenu.BottomSheetItemWithCompoundButton.Builder; -import net.osmand.plus.base.bottomsheetmenu.SimpleBottomSheetItem; -import net.osmand.plus.base.bottomsheetmenu.simpleitems.SimpleDividerItem; -import net.osmand.util.Algorithms; -import net.osmand.view.ThreeStateCheckbox; - -import java.util.ArrayList; -import java.util.List; - -import static net.osmand.view.ThreeStateCheckbox.State.CHECKED; -import static net.osmand.view.ThreeStateCheckbox.State.MISC; -import static net.osmand.view.ThreeStateCheckbox.State.UNCHECKED; - -public class SelectMultipleItemsBottomSheet extends MenuBottomSheetDialogFragment { - - private OsmandApplication app; - private UiUtilities uiUtilities; - - private TextView title; - private TextView description; - private TextView applyButtonTitle; - private TextView checkBoxTitle; - private TextView selectedSize; - private ThreeStateCheckbox checkBox; - - private int activeColorRes; - private int secondaryColorRes; - - private final List allItems = new ArrayList<>(); - private final List selectedItems = new ArrayList<>(); - private SelectionUpdateListener selectionUpdateListener; - private OnApplySelectionListener onApplySelectionListener; - - public static final String TAG = SelectMultipleItemsBottomSheet.class.getSimpleName(); - - @Nullable - @Override - public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) { - View mainView = super.onCreateView(inflater, parent, savedInstanceState); - onSelectedItemsChanged(); - return mainView; - } - - @Override - public void createMenuItems(Bundle savedInstanceState) { - app = requiredMyApplication(); - uiUtilities = app.getUIUtilities(); - activeColorRes = nightMode ? R.color.icon_color_active_dark : R.color.icon_color_active_light; - secondaryColorRes = nightMode ? R.color.icon_color_secondary_dark : R.color.icon_color_secondary_light; - - items.add(createTitleItem()); - items.add(new SimpleDividerItem(app)); - createListItems(); - } - - private BaseBottomSheetItem createTitleItem() { - LayoutInflater themedInflater = UiUtilities.getInflater(requireContext(), nightMode); - View view = themedInflater.inflate(R.layout.settings_group_title, null); - - checkBox = view.findViewById(R.id.check_box); - checkBoxTitle = view.findViewById(R.id.check_box_title); - description = view.findViewById(R.id.description); - selectedSize = view.findViewById(R.id.selected_size); - title = view.findViewById(R.id.title); - View selectAllButton = view.findViewById(R.id.select_all_button); - selectAllButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - checkBox.performClick(); - boolean checked = checkBox.getState() == CHECKED; - if (checked) { - selectedItems.addAll(allItems); - } else { - selectedItems.clear(); - } - onSelectedItemsChanged(); - updateItems(checked); - } - }); - return new SimpleBottomSheetItem.Builder().setCustomView(view).create(); - } - - private void createListItems() { - for (final SelectableItem item : allItems) { - boolean checked = selectedItems.contains(item); - final BottomSheetItemWithCompoundButton[] uiItem = new BottomSheetItemWithCompoundButton[1]; - final Builder builder = (BottomSheetItemWithCompoundButton.Builder) new Builder(); - builder.setChecked(checked) - .setButtonTintList(AndroidUtils.createCheckedColorStateList(app, secondaryColorRes, activeColorRes)) - .setLayoutId(R.layout.bottom_sheet_item_with_descr_and_checkbox_56dp) - .setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - boolean checked = !uiItem[0].isChecked(); - uiItem[0].setChecked(checked); - SelectableItem tag = (SelectableItem) uiItem[0].getTag(); - if (checked) { - selectedItems.add(tag); - } else { - selectedItems.remove(tag); - } - onSelectedItemsChanged(); - } - }) - .setTag(item); - setupListItem(builder, item); - uiItem[0] = builder.create(); - items.add(uiItem[0]); - } - } - - @Override - protected void setupRightButton() { - super.setupRightButton(); - applyButtonTitle = rightButton.findViewById(R.id.button_text); - } - - @Override - protected void onRightBottomButtonClick() { - if (onApplySelectionListener != null) { - onApplySelectionListener.onSelectionApplied(selectedItems); - } - dismiss(); - } - - private void onSelectedItemsChanged() { - updateSelectAllButton(); - updateSelectedSizeView(); - updateApplyButtonEnable(); - if (selectionUpdateListener != null) { - selectionUpdateListener.onSelectionUpdate(); - } - } - - @Override - protected int getRightBottomButtonTextId() { - return R.string.shared_string_apply; - } - - @Override - protected boolean useVerticalButtons() { - return true; - } - - private void setupListItem(Builder builder, SelectableItem item) { - builder.setTitle(item.title); - builder.setDescription(item.description); - builder.setIcon(uiUtilities.getIcon(item.iconId, activeColorRes)); - } - - private void updateSelectAllButton() { - String checkBoxTitle; - if (Algorithms.isEmpty(selectedItems)) { - checkBox.setState(UNCHECKED); - checkBoxTitle = getString(R.string.shared_string_select_all); - } else { - checkBox.setState(selectedItems.containsAll(allItems) ? CHECKED : MISC); - checkBoxTitle = getString(R.string.shared_string_deselect_all); - } - int checkBoxColor = checkBox.getState() == UNCHECKED ? secondaryColorRes : activeColorRes; - CompoundButtonCompat.setButtonTintList(checkBox, ColorStateList.valueOf(ContextCompat.getColor(app, checkBoxColor))); - this.checkBoxTitle.setText(checkBoxTitle); - } - - private void updateSelectedSizeView() { - String selected = String.valueOf(selectedItems.size()); - String all = String.valueOf(allItems.size()); - selectedSize.setText(getString(R.string.ltr_or_rtl_combine_via_slash, selected, all)); - } - - private void updateApplyButtonEnable() { - if (Algorithms.isEmpty(selectedItems)) { - rightButton.setEnabled(false); - } else { - rightButton.setEnabled(true); - } - } - - private void updateItems(boolean checked) { - for (BaseBottomSheetItem item : items) { - if (item instanceof BottomSheetItemWithCompoundButton) { - ((BottomSheetItemWithCompoundButton) item).setChecked(checked); - } - } - } - - public void setTitle(@NonNull String title) { - this.title.setText(title); - } - - public void setDescription(@NonNull String description) { - this.description.setText(description); - } - - public void setConfirmButtonTitle(@NonNull String confirmButtonTitle) { - applyButtonTitle.setText(confirmButtonTitle); - } - - private void setItems(List allItems) { - if (!Algorithms.isEmpty(allItems)) { - this.allItems.addAll(allItems); - } - } - - private void setSelectedItems(List selected) { - if (!Algorithms.isEmpty(selected)) { - this.selectedItems.addAll(selected); - } - } - - public List getSelectedItems() { - return selectedItems; - } - - @Override - public void onPause() { - super.onPause(); - if (requireActivity().isChangingConfigurations()) { - dismiss(); - } - } - - public static SelectMultipleItemsBottomSheet showInstance(@NonNull AppCompatActivity activity, - @NonNull List items, - @Nullable List selected, - boolean usedOnMap) { - SelectMultipleItemsBottomSheet fragment = new SelectMultipleItemsBottomSheet(); - fragment.setUsedOnMap(usedOnMap); - fragment.setItems(items); - fragment.setSelectedItems(selected); - FragmentManager fm = activity.getSupportFragmentManager(); - fragment.show(fm, TAG); - return fragment; - } - - public void setSelectionUpdateListener(SelectionUpdateListener selectionUpdateListener) { - this.selectionUpdateListener = selectionUpdateListener; - } - - public void setOnApplySelectionListener(OnApplySelectionListener onApplySelectionListener) { - this.onApplySelectionListener = onApplySelectionListener; - } - - public interface SelectionUpdateListener { - void onSelectionUpdate(); - } - - public interface OnApplySelectionListener { - void onSelectionApplied(List selectedItems); - } - - public static class SelectableItem { - private String title; - private String description; - private int iconId; - private Object object; - - public void setTitle(String title) { - this.title = title; - } - - public void setDescription(String description) { - this.description = description; - } - - public void setIconId(int iconId) { - this.iconId = iconId; - } - - public void setObject(Object object) { - this.object = object; - } - - public Object getObject() { - return object; - } - } - -} diff --git a/OsmAnd/src/net/osmand/plus/base/SelectionBottomSheet.java b/OsmAnd/src/net/osmand/plus/base/SelectionBottomSheet.java new file mode 100644 index 0000000000..dd6dc993eb --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/base/SelectionBottomSheet.java @@ -0,0 +1,299 @@ +package net.osmand.plus.base; + +import android.content.Context; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.LinearLayout; +import android.widget.LinearLayout.LayoutParams; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import net.osmand.plus.OsmandApplication; +import net.osmand.plus.R; +import net.osmand.plus.UiUtilities; +import net.osmand.plus.base.bottomsheetmenu.BaseBottomSheetItem; +import net.osmand.plus.base.bottomsheetmenu.SimpleBottomSheetItem; +import net.osmand.plus.base.bottomsheetmenu.simpleitems.SimpleDividerItem; +import net.osmand.plus.helpers.AndroidUiHelper; +import net.osmand.plus.widgets.multistatetoggle.MultiStateToggleButton; +import net.osmand.plus.widgets.multistatetoggle.RadioItem; +import net.osmand.plus.widgets.multistatetoggle.TextToggleButton; +import net.osmand.util.Algorithms; +import net.osmand.view.ThreeStateCheckbox; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public abstract class SelectionBottomSheet extends MenuBottomSheetDialogFragment { + + protected OsmandApplication app; + protected LayoutInflater inflater; + protected UiUtilities uiUtilities; + + protected TextView title; + protected TextView titleDescription; + protected TextView primaryDescription; + protected TextView secondaryDescription; + protected TextView selectedSize; + protected LinearLayout toggleContainer; + protected MultiStateToggleButton radioGroup; + protected View selectAllButton; + protected TextView checkBoxTitle; + protected ThreeStateCheckbox checkBox; + protected LinearLayout listContainer; + protected TextView applyButtonTitle; + + protected int activeColorRes; + protected int secondaryColorRes; + + private DialogStateListener dialogStateListener; + private OnApplySelectionListener onApplySelectionListener; + + protected List allItems = new ArrayList<>(); + protected Map listViews = new HashMap<>(); + private List modes; + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) { + View mainView = super.onCreateView(inflater, parent, savedInstanceState); + createSelectionListIfPossible(); + notifyUiCreated(); + return mainView; + } + + @Override + public void createMenuItems(Bundle savedInstanceState) { + app = requiredMyApplication(); + uiUtilities = app.getUIUtilities(); + inflater = UiUtilities.getInflater(requireContext(), nightMode); + activeColorRes = nightMode ? R.color.icon_color_active_dark : R.color.icon_color_active_light; + secondaryColorRes = nightMode ? R.color.icon_color_secondary_dark : R.color.icon_color_secondary_light; + + items.add(createHeaderView()); + if (shouldShowDivider()) { + items.add(new SimpleDividerItem(app)); + } + items.add(createSelectionView()); + } + + private BaseBottomSheetItem createHeaderView() { + View view = inflater.inflate(R.layout.settings_group_title, null); + + title = view.findViewById(R.id.title); + titleDescription = view.findViewById(R.id.title_description); + primaryDescription = view.findViewById(R.id.primary_description); + secondaryDescription = view.findViewById(R.id.secondary_description); + selectedSize = view.findViewById(R.id.selected_size); + toggleContainer = view.findViewById(R.id.custom_radio_buttons); + radioGroup = new TextToggleButton(app, toggleContainer, nightMode); + selectAllButton = view.findViewById(R.id.select_all_button); + checkBoxTitle = view.findViewById(R.id.check_box_title); + checkBox = view.findViewById(R.id.check_box); + + if (modes != null) { + radioGroup.setItems(modes); + } + + return new SimpleBottomSheetItem.Builder().setCustomView(view).create(); + } + + private BaseBottomSheetItem createSelectionView() { + Context themedCtx = UiUtilities.getThemedContext(requireContext(), nightMode); + listContainer = new LinearLayout(themedCtx); + listContainer.setLayoutParams(new LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT)); + listContainer.setOrientation(LinearLayout.VERTICAL); + return new SimpleBottomSheetItem.Builder().setCustomView(listContainer).create(); + } + + public void setTitle(@NonNull String title) { + this.title.setText(title); + } + + public void setTitleDescription(@NonNull String description) { + titleDescription.setText(description); + } + + public void setPrimaryDescription(@NonNull String description) { + primaryDescription.setText(description); + } + + public void setSecondaryDescription(@NonNull String description) { + secondaryDescription.setText(description); + } + + public void setApplyButtonTitle(@NonNull String title) { + applyButtonTitle.setText(title); + } + + public void setModes(@NonNull List modes) { + this.modes = modes; + if (radioGroup != null) { + radioGroup.setItems(modes); + } + } + + public void setSelectedMode(@NonNull RadioItem mode) { + radioGroup.setSelectedItem(mode); + } + + public void setItems(List allItems) { + this.allItems.clear(); + if (!Algorithms.isEmpty(allItems)) { + this.allItems.addAll(allItems); + createSelectionListIfPossible(); + } + } + + public void setDialogStateListener(DialogStateListener dialogStateListener) { + this.dialogStateListener = dialogStateListener; + } + + public void setOnApplySelectionListener(OnApplySelectionListener onApplySelectionListener) { + this.onApplySelectionListener = onApplySelectionListener; + } + + private void createSelectionListIfPossible() { + if (listContainer != null && allItems != null) { + recreateList(); + } + } + + private void recreateList() { + listViews.clear(); + listContainer.removeAllViews(); + for (SelectableItem item : allItems) { + setupItemView(item, inflater.inflate(getItemLayoutId(), null)); + } + } + + private void setupItemView(SelectableItem item, View view) { + updateItemView(item, view); + listViews.put(item, view); + listContainer.addView(view); + } + + public List getAllItems() { + return allItems; + } + + @NonNull + public abstract List getSelectedItems(); + + protected abstract void updateItemView(SelectableItem item, View view); + + protected abstract int getItemLayoutId(); + + protected abstract boolean shouldShowDivider(); + + protected void notifyUiCreated() { + if (dialogStateListener != null) { + dialogStateListener.onDialogCreated(); + } + } + + protected void showElements(View... views) { + AndroidUiHelper.setVisibility(View.VISIBLE, views); + } + + protected void hideElements(View... views) { + AndroidUiHelper.setVisibility(View.GONE, views); + } + + @Override + protected void setupRightButton() { + super.setupRightButton(); + applyButtonTitle = rightButton.findViewById(R.id.button_text); + } + + @Override + protected void onRightBottomButtonClick() { + if (onApplySelectionListener != null) { + onApplySelectionListener.onSelectionApplied(getSelectedItems()); + } + dismiss(); + } + + @Override + protected int getRightBottomButtonTextId() { + return R.string.shared_string_apply; + } + + @Override + protected boolean useVerticalButtons() { + return true; + } + + @Override + public void onPause() { + super.onPause(); + if (requireActivity().isChangingConfigurations()) { + dismiss(); + } + } + + @Override + public void onDestroy() { + if (dialogStateListener != null) { + dialogStateListener.onCloseDialog(); + } + super.onDestroy(); + } + + public interface DialogStateListener { + void onDialogCreated(); + void onCloseDialog(); + } + + public interface OnApplySelectionListener { + void onSelectionApplied(List selectedItems); + } + + public static class SelectableItem { + private String title; + private String description; + private int iconId; + private Object object; + + public String getTitle() { + return title; + } + + public String getDescription() { + return description; + } + + public int getIconId() { + return iconId; + } + + public Object getObject() { + return object; + } + + public void setTitle(String title) { + this.title = title; + } + + public void setDescription(String description) { + this.description = description; + } + + public void setIconId(int iconId) { + this.iconId = iconId; + } + + public void setObject(Object object) { + this.object = object; + } + } + +} diff --git a/OsmAnd/src/net/osmand/plus/base/bottomsheetmenu/BaseBottomSheetItem.java b/OsmAnd/src/net/osmand/plus/base/bottomsheetmenu/BaseBottomSheetItem.java index 1914756d21..f77fc8503f 100644 --- a/OsmAnd/src/net/osmand/plus/base/bottomsheetmenu/BaseBottomSheetItem.java +++ b/OsmAnd/src/net/osmand/plus/base/bottomsheetmenu/BaseBottomSheetItem.java @@ -32,6 +32,10 @@ public class BaseBottomSheetItem { return tag; } + public void setTag(Object tag) { + this.tag = tag; + } + public BaseBottomSheetItem(View view, @LayoutRes int layoutId, Object tag, diff --git a/OsmAnd/src/net/osmand/plus/dashboard/DashChooseAppDirFragment.java b/OsmAnd/src/net/osmand/plus/dashboard/DashChooseAppDirFragment.java index bd8918ac4d..34c5659a4f 100644 --- a/OsmAnd/src/net/osmand/plus/dashboard/DashChooseAppDirFragment.java +++ b/OsmAnd/src/net/osmand/plus/dashboard/DashChooseAppDirFragment.java @@ -11,7 +11,6 @@ import android.content.DialogInterface; import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; -import android.os.StatFs; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; @@ -34,10 +33,10 @@ import net.osmand.FileUtils; import net.osmand.PlatformUtil; import net.osmand.ValueHolder; import net.osmand.plus.OsmandApplication; -import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.ProgressImplementation; import net.osmand.plus.R; import net.osmand.plus.download.DownloadActivity; +import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.util.Algorithms; import org.apache.commons.logging.Log; @@ -93,18 +92,6 @@ public class DashChooseAppDirFragment { selectePathTemp = null; } - private String getFreeSpace(File dir) { - if (dir.canRead()) { - try { - StatFs fs = new StatFs(dir.getAbsolutePath()); - return AndroidUtils.formatSize(activity, (long) fs.getAvailableBlocks() * fs.getBlockSize()); - } catch (IllegalArgumentException e) { - LOG.error(e); - } - } - return ""; - } - public void updateView() { if (type == OsmandSettings.EXTERNAL_STORAGE_TYPE_INTERNAL_FILE) { locationPath.setText(R.string.storage_directory_internal_app); @@ -117,7 +104,7 @@ public class DashChooseAppDirFragment { } else if (type == OsmandSettings.EXTERNAL_STORAGE_TYPE_SPECIFIED) { locationPath.setText(R.string.storage_directory_manual); } - locationDesc.setText(selectedFile.getAbsolutePath() + " \u2022 " + getFreeSpace(selectedFile)); + locationDesc.setText(selectedFile.getAbsolutePath() + " \u2022 " + AndroidUtils.getFreeSpace(activity, selectedFile)); boolean copyFiles = !currentAppFile.getAbsolutePath().equals(selectedFile.getAbsolutePath()) && !mapsCopied; warningReadonly.setVisibility(copyFiles ? View.VISIBLE : View.GONE); if (copyFiles) { diff --git a/OsmAnd/src/net/osmand/plus/development/TestBackupActivity.java b/OsmAnd/src/net/osmand/plus/development/TestBackupActivity.java index 0f1d1775bd..3b38b1469b 100644 --- a/OsmAnd/src/net/osmand/plus/development/TestBackupActivity.java +++ b/OsmAnd/src/net/osmand/plus/development/TestBackupActivity.java @@ -1,12 +1,9 @@ package net.osmand.plus.development; import android.app.Activity; -import android.app.ProgressDialog; import android.graphics.drawable.Drawable; -import android.os.AsyncTask; import android.os.Bundle; -import android.text.TextUtils; -import android.util.Patterns; +import android.util.Pair; import android.util.TypedValue; import android.view.View; import android.widget.EditText; @@ -17,56 +14,58 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.widget.Toolbar; -import net.osmand.AndroidNetworkUtils; -import net.osmand.AndroidNetworkUtils.OnFilesUploadCallback; -import net.osmand.AndroidNetworkUtils.OnRequestResultListener; import net.osmand.AndroidUtils; -import net.osmand.IndexConstants; import net.osmand.plus.OsmandApplication; -import net.osmand.plus.ProgressImplementation; import net.osmand.plus.R; import net.osmand.plus.UiUtilities; import net.osmand.plus.UiUtilities.DialogButtonType; import net.osmand.plus.activities.OsmandActionBarActivity; +import net.osmand.plus.backup.BackupHelper; +import net.osmand.plus.backup.BackupHelper.BackupInfo; +import net.osmand.plus.backup.BackupHelper.OnRegisterUserListener; +import net.osmand.plus.backup.BackupTask; +import net.osmand.plus.backup.BackupTask.OnBackupListener; +import net.osmand.plus.backup.GpxFileInfo; +import net.osmand.plus.backup.PrepareBackupTask; +import net.osmand.plus.backup.PrepareBackupTask.OnPrepareBackupListener; +import net.osmand.plus.backup.UserFile; import net.osmand.plus.settings.backend.OsmandSettings; +import net.osmand.plus.widgets.OsmandTextFieldBoxes; import net.osmand.util.Algorithms; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - import java.io.File; import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; +import java.text.DateFormat; +import java.util.Date; import java.util.Map; +import java.util.Map.Entry; public class TestBackupActivity extends OsmandActionBarActivity { - // TODO pass actual sub order id! - private static final String TEST_ORDER_ID = ""; + private static final DateFormat DF = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM); private OsmandApplication app; private OsmandSettings settings; + private BackupHelper backupHelper; private ProgressBar progressBar; private View buttonRegister; private View buttonVerify; + private View buttonRefresh; private View buttonBackup; private View buttonRestore; private EditText emailEditText; + private OsmandTextFieldBoxes tokenEdit; private EditText tokenEditText; private TextView infoView; - public interface OnResultListener { - void onResult(boolean success, @Nullable String result); - } + private BackupInfo backupInfo; @Override public void onCreate(Bundle savedInstanceState) { app = getMyApplication(); settings = app.getSettings(); + backupHelper = app.getBackupHelper(); final WeakReference activityRef = new WeakReference<>(this); boolean nightMode = !app.getSettings().isLightContent(); @@ -94,11 +93,14 @@ public class TestBackupActivity extends OsmandActionBarActivity { UiUtilities.setupDialogButton(nightMode, buttonRegister, DialogButtonType.PRIMARY, "Register"); buttonVerify = findViewById(R.id.btn_verify); UiUtilities.setupDialogButton(nightMode, buttonVerify, DialogButtonType.PRIMARY, "Verify"); + buttonRefresh = findViewById(R.id.btn_refresh); + UiUtilities.setupDialogButton(nightMode, buttonRefresh, DialogButtonType.PRIMARY, "Refresh"); buttonBackup = findViewById(R.id.btn_backup); UiUtilities.setupDialogButton(nightMode, buttonBackup, DialogButtonType.PRIMARY, "Backup"); buttonRestore = findViewById(R.id.btn_restore); UiUtilities.setupDialogButton(nightMode, buttonRestore, DialogButtonType.PRIMARY, "Restore"); + tokenEdit = findViewById(R.id.edit_token_label); tokenEditText = findViewById(R.id.edit_token); infoView = findViewById(R.id.text_info); progressBar = findViewById(R.id.progress_bar); @@ -109,22 +111,31 @@ public class TestBackupActivity extends OsmandActionBarActivity { if (!Algorithms.isEmpty(email)) { emailEditText.setText(email); } + if (backupHelper.isRegistered()) { + tokenEdit.setVisibility(View.GONE); + buttonVerify.setVisibility(View.GONE); + } else { + tokenEdit.setVisibility(View.VISIBLE); + buttonVerify.setVisibility(View.VISIBLE); + } buttonRegister.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { String email = emailEditText.getText().toString(); - if (isEmailValid(email)) { + if (AndroidUtils.isValidEmail(email)) { buttonRegister.setEnabled(false); settings.BACKUP_USER_EMAIL.set(email); progressBar.setVisibility(View.VISIBLE); - registerUser(email, new OnResultListener() { + backupHelper.registerUser(email, new OnRegisterUserListener() { @Override - public void onResult(boolean success, @Nullable String result) { + public void onRegisterUser(int status, @Nullable String message) { TestBackupActivity a = activityRef.get(); if (AndroidUtils.isActivityNotDestroyed(a)) { a.progressBar.setVisibility(View.GONE); - a.buttonRegister.setEnabled(!success); - a.buttonVerify.setEnabled(success); + a.buttonRegister.setEnabled(status != BackupHelper.STATUS_SUCCESS); + a.tokenEdit.setVisibility(View.VISIBLE); + a.buttonVerify.setVisibility(View.VISIBLE); + a.buttonVerify.setEnabled(status == BackupHelper.STATUS_SUCCESS); a.tokenEditText.requestFocus(); } } @@ -139,17 +150,22 @@ public class TestBackupActivity extends OsmandActionBarActivity { @Override public void onClick(View v) { String token = tokenEditText.getText().toString(); - if (isTokenValid(token)) { + if (BackupHelper.isTokenValid(token)) { buttonVerify.setEnabled(false); progressBar.setVisibility(View.VISIBLE); - registerDevice(token, new OnResultListener() { + backupHelper.registerDevice(token, new BackupHelper.OnRegisterDeviceListener() { + @Override - public void onResult(boolean success, @Nullable String result) { + public void onRegisterDevice(int status, @Nullable String message) { TestBackupActivity a = activityRef.get(); if (AndroidUtils.isActivityNotDestroyed(a)) { a.progressBar.setVisibility(View.GONE); - a.buttonVerify.setEnabled(!success); - a.loadBackupInfo(); + a.buttonVerify.setEnabled(status != BackupHelper.STATUS_SUCCESS); + if (status == BackupHelper.STATUS_SUCCESS) { + tokenEdit.setVisibility(View.GONE); + buttonVerify.setVisibility(View.GONE); + } + a.prepareBackup(); } } }); @@ -160,262 +176,163 @@ public class TestBackupActivity extends OsmandActionBarActivity { } } }); + buttonRefresh.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + prepareBackup(); + } + }); buttonBackup.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - uploadFiles(); + if (backupInfo != null) { + BackupTask task = new BackupTask(backupInfo, TestBackupActivity.this, new OnBackupListener() { + @Override + public void onBackupDone(@Nullable Map uploadErrors, @Nullable Map downloadErrors, + @Nullable Map deleteErrors, @Nullable String error) { + TestBackupActivity a = activityRef.get(); + if (AndroidUtils.isActivityNotDestroyed(a)) { + String description; + if (error != null) { + description = error; + } else if (uploadErrors == null && downloadErrors == null) { + description = "No data"; + } else { + description = getBackupErrorsDescription(uploadErrors, downloadErrors, deleteErrors, error); + } + a.infoView.setText(description); + a.infoView.requestFocus(); + a.prepareBackup(); + } + } + }); + task.runBackup(); + } } }); buttonRestore.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - } - }); - - loadBackupInfo(); - } - - private void loadBackupInfo() { - if (!Algorithms.isEmpty(getDeviceId()) && !Algorithms.isEmpty(getAccessToken())) { - final WeakReference activityRef = new WeakReference<>(this); - progressBar.setVisibility(View.VISIBLE); - loadBackupInfo(new OnResultListener() { - @Override - public void onResult(boolean success, @Nullable String result) { - TestBackupActivity a = activityRef.get(); - if (AndroidUtils.isActivityNotDestroyed(a)) { - a.progressBar.setVisibility(View.GONE); - a.infoView.setText(result); - a.infoView.requestFocus(); - } - } - }); - } - } - - private boolean isEmailValid(CharSequence target) { - return (!TextUtils.isEmpty(target) && Patterns.EMAIL_ADDRESS.matcher(target).matches()); - } - - private String getOrderId() { - return TEST_ORDER_ID; - } - - private String getDeviceId() { - return settings.BACKUP_DEVICE_ID.get(); - } - - private String getAccessToken() { - return settings.BACKUP_ACCESS_TOKEN.get(); - } - - private void registerUser(@NonNull String email, @Nullable final OnResultListener listener) { - Map params = new HashMap<>(); - params.put("email", email); - params.put("orderid", getOrderId()); - params.put("deviceid", app.getUserAndroidId()); - AndroidNetworkUtils.sendRequestAsync(app, "https://osmand.net/userdata/user-register", params, "Register user", true, true, new OnRequestResultListener() { - @Override - public void onResult(String resultJson) { - boolean success = false; - if (!Algorithms.isEmpty(resultJson)) { - try { - // {"status":"ok"} - JSONObject result = new JSONObject(resultJson); - String status = result.getString("status"); - success = status.equals("ok"); - app.showToastMessage(success - ? "You have been registered successfully. Please check for email with activation code." - : "User registration error: " + status); - } catch (JSONException e) { - app.showToastMessage("User registration error: json parsing"); - } - } else { - app.showToastMessage("User registration error: empty response"); - } - if (listener != null) { - listener.onResult(success, resultJson); - } - } - }); - } - - private void registerDevice(String token, @Nullable final OnResultListener listener) { - Map params = new HashMap<>(); - params.put("email", settings.BACKUP_USER_EMAIL.get()); - params.put("orderid", getOrderId()); - params.put("deviceid", app.getUserAndroidId()); - params.put("token", token); - AndroidNetworkUtils.sendRequestAsync(app, "https://osmand.net/userdata/device-register", params, "Register device", true, true, new OnRequestResultListener() { - @Override - public void onResult(String resultJson) { - boolean success = false; - if (!Algorithms.isEmpty(resultJson)) { - try { - /* - { - "id": 1034, - "userid": 1033, - "deviceid": "2fa8080d2985a777", - "orderid": "460000687003939", - "accesstoken": "4bc0a61f-397a-4c3e-9ffc-db382ec00372", - "udpatetime": "Apr 11, 2021, 11:32:20 AM" - } - */ - JSONObject result = new JSONObject(resultJson); - settings.BACKUP_DEVICE_ID.set(result.getString("id")); - settings.BACKUP_USER_ID.set(result.getString("userid")); - settings.BACKUP_NATIVE_DEVICE_ID.set(result.getString("deviceid")); - settings.BACKUP_ACCESS_TOKEN.set(result.getString("accesstoken")); - settings.BACKUP_ACCESS_TOKEN_UPDATE_TIME.set(result.getString("udpatetime")); - success = true; - app.showToastMessage("Device have been registered successfully"); - } catch (JSONException e) { - app.showToastMessage("Device registration error: json parsing"); - } - } else { - app.showToastMessage("Device registration error: empty response"); - } - if (listener != null) { - listener.onResult(success, resultJson); - } - } - }); - } - - private void uploadFiles() { - LoadGpxTask loadGpxTask = new LoadGpxTask(this, new LoadGpxTask.OnLoadGpxListener() { - @Override - public void onLoadGpxDone(@NonNull List result) { - uploadFiles(result); - } - }); - loadGpxTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, this); - } - - private void uploadFiles(List gpxFiles) { - //{"status":"ok"} - final WeakReference activityRef = new WeakReference<>(this); - - Map params = new HashMap<>(); - params.put("deviceid", getDeviceId()); - params.put("accessToken", getAccessToken()); - Map headers = new HashMap<>(); - headers.put("Accept-Encoding", "deflate, gzip"); - - final Map gpxInfos = new HashMap<>(); - for (GpxInfo gpxFile : gpxFiles) { - gpxInfos.put(gpxFile.file, gpxFile); - } - final List files = new ArrayList<>(gpxInfos.keySet()); - File favoritesFile = app.getFavorites().getExternalFile(); - files.add(favoritesFile); - - final ProgressImplementation progress = ProgressImplementation.createProgressDialog(this, - "Create backup", "Uploading " + files.size() + " file(s) to server", ProgressDialog.STYLE_HORIZONTAL); - - AndroidNetworkUtils.uploadFilesAsync("https://osmand.net/userdata/upload-file", files, true, params, headers, new OnFilesUploadCallback() { - @Nullable - @Override - public Map getAdditionalParams(@NonNull File file) { - GpxInfo gpxInfo = gpxInfos.get(file); - Map additionaParams = new HashMap<>(); - additionaParams.put("name", gpxInfo == null ? file.getName() : gpxInfo.getFileName(true)); - additionaParams.put("type", Algorithms.getFileExtension(file)); - return additionaParams; - } - - @Override - public void onFileUploadProgress(@NonNull File file, int percent) { - Activity a = activityRef.get(); - if (AndroidUtils.isActivityNotDestroyed(a)) { - if (percent < 100) { - progress.startTask(file.getName(), percent); - } else { - progress.finishTask(); - } - } - } - - @Override - public void onFilesUploadDone(@NonNull Map errors) { - Activity a = activityRef.get(); - if (AndroidUtils.isActivityNotDestroyed(a)) { - app.runInUIThread(new Runnable() { + if (backupInfo != null) { + BackupTask task = new BackupTask(backupInfo, TestBackupActivity.this, new OnBackupListener() { @Override - public void run() { - try { - if (progress.getDialog().isShowing()) { - progress.getDialog().dismiss(); + public void onBackupDone(@Nullable Map uploadErrors, @Nullable Map downloadErrors, + @Nullable Map deleteErrors, @Nullable String error) { + TestBackupActivity a = activityRef.get(); + if (AndroidUtils.isActivityNotDestroyed(a)) { + String description; + if (error != null) { + description = error; + } else if (uploadErrors == null && downloadErrors == null) { + description = "No data"; + } else { + description = getBackupErrorsDescription(uploadErrors, downloadErrors, deleteErrors, error); } - } catch (Exception e) { - //ignored + a.infoView.setText(description); + a.infoView.requestFocus(); + a.prepareBackup(); } } - }, 300); - app.showToastMessage("Uploaded " + (files.size() - errors.size() + " files" + - (errors.size() > 0 ? ". Errors: " + errors.size() : ""))); - loadBackupInfo(); + }); + task.runRestore(); } } }); + + prepareBackup(); } - private void loadBackupInfo(@Nullable final OnResultListener listener) { - Map params = new HashMap<>(); - params.put("deviceid", getDeviceId()); - params.put("accessToken", getAccessToken()); - AndroidNetworkUtils.sendRequestAsync(app, "https://osmand.net/userdata/list-files", params, "Get backup info", true, false, new OnRequestResultListener() { + private String getBackupErrorsDescription(@Nullable Map uploadErrors, @Nullable Map downloadErrors, @Nullable Map deleteErrors, @Nullable String error) { + StringBuilder sb = new StringBuilder(); + if (!Algorithms.isEmpty(uploadErrors)) { + sb.append("--- Upload errors ---").append("\n"); + for (Entry uploadEntry : uploadErrors.entrySet()) { + sb.append(uploadEntry.getKey().getName()).append(": ").append(uploadEntry.getValue()).append("\n"); + } + } + if (!Algorithms.isEmpty(downloadErrors)) { + sb.append("--- Download errors ---").append("\n"); + for (Entry downloadEntry : downloadErrors.entrySet()) { + sb.append(downloadEntry.getKey().getName()).append(": ").append(downloadEntry.getValue()).append("\n"); + } + } + if (!Algorithms.isEmpty(deleteErrors)) { + sb.append("--- Delete errors ---").append("\n"); + for (Entry deleteEntry : deleteErrors.entrySet()) { + sb.append(deleteEntry.getKey().getName()).append(": ").append(deleteEntry.getValue()).append("\n"); + } + } + return sb.length() == 0 ? "OK" : sb.toString(); + } + + private String getBackupDescription(@NonNull BackupInfo backupInfo) { + StringBuilder sb = new StringBuilder(); + if (!Algorithms.isEmpty(backupInfo.filesToUpload)) { + sb.append("\n").append("--- Upload ---").append("\n"); + for (GpxFileInfo info : backupInfo.filesToUpload) { + sb.append(info.getFileName(true)) + .append(" L: ").append(DF.format(new Date(info.getFileDate()))) + .append(" U: ").append(DF.format(new Date(info.uploadTime))) + .append("\n"); + } + } + if (!Algorithms.isEmpty(backupInfo.filesToDownload)) { + sb.append("\n").append("--- Download ---").append("\n"); + for (UserFile userFile : backupInfo.filesToDownload) { + sb.append(userFile.getName()) + .append(" R: ").append(DF.format(new Date(userFile.getClienttimems()))) + .append("\n"); + } + } + if (!Algorithms.isEmpty(backupInfo.filesToDelete)) { + sb.append("\n").append("--- Delete ---").append("\n"); + for (UserFile userFile : backupInfo.filesToDelete) { + sb.append(userFile.getName()) + .append(" R: ").append(DF.format(new Date(userFile.getClienttimems()))) + .append("\n"); + } + } + if (!Algorithms.isEmpty(backupInfo.filesToMerge)) { + sb.append("\n").append("--- Conflicts ---").append("\n"); + for (Pair localRemote : backupInfo.filesToMerge) { + GpxFileInfo local = localRemote.first; + UserFile remote = localRemote.second; + sb.append(local.getFileName(true)) + .append(" L: ").append(DF.format(new Date(local.getFileDate()))) + .append(" U: ").append(DF.format(new Date(local.uploadTime))) + .append(" R: ").append(DF.format(new Date(remote.getClienttimems()))) + .append("\n"); + } + } + return sb.toString(); + } + + private void prepareBackup() { + final WeakReference activityRef = new WeakReference<>(this); + PrepareBackupTask prepareBackupTask = new PrepareBackupTask(this, new OnPrepareBackupListener() { @Override - public void onResult(String resultJson) { - boolean success = false; - StringBuilder resultString = new StringBuilder(); - if (!Algorithms.isEmpty(resultJson)) { - try { - /* - { - "totalZipSize": 21792, - "totalFileSize": 185920, - "totalFiles": 1, - "totalFileVersions": 2, - "uniqueFiles": [ - { - "userid": 1033, - "id": 7, - "deviceid": 1034, - "filesize": 92960, - "type": "gpx", - "name": "test/Day 2.gpx", - "updatetime": "Apr 11, 2021, 1:49:01 PM", - "updatetimems": 1618141741822, - "zipSize": 10896 - } - ], - "deviceid": 1034 - } - */ - JSONObject result = new JSONObject(resultJson); - String totalZipSize = result.getString("totalZipSize"); - String totalFiles = result.getString("totalFiles"); - String totalFileVersions = result.getString("totalFileVersions"); - JSONArray files = result.getJSONArray("uniqueFiles"); - resultString.append("Total files: ").append(totalFiles).append("\n"); - resultString.append("Total zip size: ").append(AndroidUtils.formatSize(app, Long.parseLong(totalZipSize))).append("\n"); - resultString.append("Total file versions: ").append(totalFileVersions); - - success = true; - } catch (JSONException e) { + public void onBackupPrepared(@Nullable BackupInfo backupInfo, @Nullable String error) { + TestBackupActivity.this.backupInfo = backupInfo; + TestBackupActivity a = activityRef.get(); + if (AndroidUtils.isActivityNotDestroyed(a)) { + String description = "Last uploaded: " + DF.format(new Date(settings.BACKUP_LAST_UPLOADED_TIME.get())) + "\n\n"; + if (error != null) { + description += error; + } else if (backupInfo == null) { + description += "No data"; + } else { + description += "Files to upload: " + backupInfo.filesToUpload.size() + + "\nFiles to download: " + backupInfo.filesToDownload.size() + + "\nFiles to delete: " + backupInfo.filesToDelete.size() + + "\nConflicts: " + backupInfo.filesToMerge.size() + + "\n" + getBackupDescription(backupInfo); } - } - if (listener != null) { - listener.onResult(success, resultString.toString()); + a.infoView.setText(description); + a.infoView.requestFocus(); } } }); - } - - private boolean isTokenValid(String token) { - return token.matches("[0-9]+"); + prepareBackupTask.prepare(); } private int resolveResourceId(final Activity activity, final int attr) { @@ -423,173 +340,4 @@ public class TestBackupActivity extends OsmandActionBarActivity { activity.getTheme().resolveAttribute(attr, typedvalueattr, true); return typedvalueattr.resourceId; } - - private static class LoadGpxTask extends AsyncTask> { - - private final OsmandApplication app; - private final OnLoadGpxListener listener; - private final WeakReference activityRef; - private List result; - private ProgressImplementation progress; - - interface OnLoadGpxListener { - void onLoadGpxDone(@NonNull List result); - } - - LoadGpxTask(@NonNull Activity activity, @Nullable OnLoadGpxListener listener) { - this.activityRef = new WeakReference<>(activity); - this.app = (OsmandApplication) activity.getApplication(); - this.listener = listener; - } - - public List getResult() { - return result; - } - - @NonNull - @Override - protected List doInBackground(Activity... params) { - List result = new ArrayList<>(); - loadGPXData(app.getAppPath(IndexConstants.GPX_INDEX_DIR), result, this); - return result; - } - - public void loadFile(GpxInfo... loaded) { - publishProgress(loaded); - } - - @Override - protected void onPreExecute() { - Activity a = activityRef.get(); - if (AndroidUtils.isActivityNotDestroyed(a)) { - progress = ProgressImplementation.createProgressDialog(a, - "Create backup", "Collecting gpx files...", ProgressDialog.STYLE_HORIZONTAL); - } - } - - @Override - protected void onProgressUpdate(GpxInfo... values) { - Activity a = activityRef.get(); - if (AndroidUtils.isActivityNotDestroyed(a)) { - progress.startTask(values[0].getFileName(true), -1); - } - } - - @Override - protected void onPostExecute(@NonNull List result) { - this.result = result; - if (listener != null) { - listener.onLoadGpxDone(result); - } - Activity a = activityRef.get(); - if (AndroidUtils.isActivityNotDestroyed(a)) { - progress.finishTask(); - app.runInUIThread(new Runnable() { - @Override - public void run() { - try { - if (progress.getDialog().isShowing()) { - progress.getDialog().dismiss(); - } - } catch (Exception e) { - //ignored - } - } - }, 300); - } - } - - private void loadGPXData(File mapPath, List result, LoadGpxTask loadTask) { - if (mapPath.canRead()) { - List progress = new ArrayList<>(); - loadGPXFolder(mapPath, result, loadTask, progress, ""); - if (!progress.isEmpty()) { - loadTask.loadFile(progress.toArray(new GpxInfo[0])); - } - } - } - - private void loadGPXFolder(File mapPath, List result, LoadGpxTask loadTask, List progress, - String gpxSubfolder) { - File[] listFiles = mapPath.listFiles(); - if (listFiles != null) { - for (File gpxFile : listFiles) { - if (gpxFile.isDirectory()) { - String sub = gpxSubfolder.length() == 0 ? gpxFile.getName() : gpxSubfolder + "/" - + gpxFile.getName(); - loadGPXFolder(gpxFile, result, loadTask, progress, sub); - } else if (gpxFile.isFile() && gpxFile.getName().toLowerCase().endsWith(IndexConstants.GPX_FILE_EXT)) { - GpxInfo info = new GpxInfo(); - info.subfolder = gpxSubfolder; - info.file = gpxFile; - result.add(info); - progress.add(info); - if (progress.size() > 7) { - loadTask.loadFile(progress.toArray(new GpxInfo[0])); - progress.clear(); - } - } - } - } - } - } - - private static class GpxInfo { - public File file; - public String subfolder; - - private String name = null; - private int sz = -1; - private String fileName = null; - - public String getName() { - if (name == null) { - name = formatName(file.getName()); - } - return name; - } - - private String formatName(String name) { - int ext = name.lastIndexOf('.'); - if (ext != -1) { - name = name.substring(0, ext); - } - return name.replace('_', ' '); - } - - // Usage: AndroidUtils.formatSize(v.getContext(), getSize() * 1024l); - public int getSize() { - if (sz == -1) { - if (file == null) { - return -1; - } - sz = (int) ((file.length() + 512) >> 10); - } - return sz; - } - - public long getFileDate() { - if (file == null) { - return 0; - } - return file.lastModified(); - } - - public String getFileName(boolean includeSubfolder) { - String result; - if (fileName != null) { - result = fileName; - } else { - if (file == null) { - result = ""; - } else { - result = fileName = file.getName(); - } - } - if (includeSubfolder && !Algorithms.isEmpty(subfolder)) { - result = subfolder + "/" + result; - } - return result; - } - } } diff --git a/OsmAnd/src/net/osmand/plus/dialogs/ConfigureMapMenu.java b/OsmAnd/src/net/osmand/plus/dialogs/ConfigureMapMenu.java index 08d37e556a..55d6e0219b 100644 --- a/OsmAnd/src/net/osmand/plus/dialogs/ConfigureMapMenu.java +++ b/OsmAnd/src/net/osmand/plus/dialogs/ConfigureMapMenu.java @@ -530,7 +530,7 @@ public class ConfigureMapMenu { TextView switchText = (TextView) v.findViewById(R.id.switchText); switchText.setText(activity.getString(R.string.translit_name_if_miss, txtValues[position])); SwitchCompat check = (SwitchCompat) v.findViewById(R.id.check); - check.setChecked(settings.MAP_TRANSLITERATE_NAMES.isSet() ? transliterateNames : txtIds[position].equals("en")); + check.setChecked(transliterateNames); check.setOnCheckedChangeListener(translitChangdListener); UiUtilities.setupCompoundButton(nightMode, selectedProfileColor, check); } else { @@ -548,6 +548,7 @@ public class ConfigureMapMenu { @Override public void onClick(DialogInterface dialog, int which) { selectedLanguageIndex = which; + transliterateNames = settings.MAP_TRANSLITERATE_NAMES.isSet() ? transliterateNames : txtIds[which].equals("en"); ((AlertDialog) dialog).getListView().setSelection(which); singleChoiceAdapter.notifyDataSetChanged(); } diff --git a/OsmAnd/src/net/osmand/plus/download/DownloadActivity.java b/OsmAnd/src/net/osmand/plus/download/DownloadActivity.java index ed45d50e84..65e6a8f855 100644 --- a/OsmAnd/src/net/osmand/plus/download/DownloadActivity.java +++ b/OsmAnd/src/net/osmand/plus/download/DownloadActivity.java @@ -652,7 +652,7 @@ public class DownloadActivity extends AbstractDownloadActivity implements Downlo TextView messageTextView = (TextView) view.findViewById(R.id.leftTextView); ProgressBar sizeProgress = (ProgressBar) view.findViewById(R.id.progressBar); - File dir = activity.getMyApplication().getAppPath("").getParentFile(); + File dir = activity.getMyApplication().getAppPath(null); String size = ""; int percent = 0; if (dir.canRead()) { diff --git a/OsmAnd/src/net/osmand/plus/download/DownloadActivityType.java b/OsmAnd/src/net/osmand/plus/download/DownloadActivityType.java index 071a856454..c5c4dab186 100644 --- a/OsmAnd/src/net/osmand/plus/download/DownloadActivityType.java +++ b/OsmAnd/src/net/osmand/plus/download/DownloadActivityType.java @@ -10,6 +10,7 @@ import net.osmand.map.OsmandRegions; import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; import net.osmand.plus.Version; +import net.osmand.plus.activities.LocalIndexInfo; import net.osmand.plus.helpers.FileNameTranslationHelper; import net.osmand.util.Algorithms; @@ -27,11 +28,12 @@ import java.util.Locale; import java.util.Map; import static net.osmand.IndexConstants.BINARY_MAP_INDEX_EXT; +import static net.osmand.plus.activities.LocalIndexHelper.LocalIndexType.SRTM_DATA; public class DownloadActivityType { private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd.MM.yyyy", Locale.US); private static Map byTag = new HashMap<>(); - + public static final DownloadActivityType NORMAL_FILE = new DownloadActivityType(R.string.download_regular_maps, "map", 10); public static final DownloadActivityType VOICE_FILE = @@ -83,7 +85,7 @@ public class DownloadActivityType { iconResource = R.drawable.ic_map; } - public int getStringResource(){ + public int getStringResource() { return stringResource; } @@ -101,7 +103,7 @@ public class DownloadActivityType { public static boolean isCountedInDownloads(IndexItem es) { DownloadActivityType tp = es.getType(); - if(tp == NORMAL_FILE || tp == ROADS_FILE){ + if (tp == NORMAL_FILE || tp == ROADS_FILE) { if (!es.extra) { return true; } @@ -120,17 +122,17 @@ public class DownloadActivityType { public static Collection values() { return byTag.values(); } - + protected static String addVersionToExt(String ext, int version) { return "_" + version + ext; } - + public boolean isAccepted(String fileName) { - if(NORMAL_FILE == this) { - return fileName.endsWith(addVersionToExt(IndexConstants.BINARY_MAP_INDEX_EXT_ZIP, IndexConstants.BINARY_MAP_VERSION)) + if (NORMAL_FILE == this) { + return fileName.endsWith(addVersionToExt(IndexConstants.BINARY_MAP_INDEX_EXT_ZIP, IndexConstants.BINARY_MAP_VERSION)) || fileName.endsWith(IndexConstants.EXTRA_ZIP_EXT) || fileName.endsWith(IndexConstants.SQLITE_EXT); - } else if(ROADS_FILE == this) { + } else if (ROADS_FILE == this) { return fileName.endsWith(addVersionToExt(IndexConstants.BINARY_ROAD_MAP_INDEX_EXT_ZIP, IndexConstants.BINARY_MAP_VERSION)); } else if (VOICE_FILE == this) { return fileName.endsWith(addVersionToExt(IndexConstants.VOICE_INDEX_EXT_ZIP, IndexConstants.VOICE_VERSION)); @@ -145,8 +147,9 @@ public class DownloadActivityType { return fileName.endsWith(addVersionToExt(IndexConstants.BINARY_TRAVEL_GUIDE_MAP_INDEX_EXT_ZIP, IndexConstants.BINARY_MAP_VERSION)); } else if (SRTM_COUNTRY_FILE == this) { - return fileName.endsWith(addVersionToExt(IndexConstants.BINARY_SRTM_MAP_INDEX_EXT_ZIP, - IndexConstants.BINARY_MAP_VERSION)); + boolean srtm = fileName.endsWith(addVersionToExt(IndexConstants.BINARY_SRTM_MAP_INDEX_EXT_ZIP, IndexConstants.BINARY_MAP_VERSION)); + boolean srtmf = fileName.endsWith(addVersionToExt(IndexConstants.BINARY_SRTM_FEET_MAP_INDEX_EXT_ZIP, IndexConstants.BINARY_MAP_VERSION)); + return srtm || srtmf; } else if (HILLSHADE_FILE == this) { return fileName.endsWith(IndexConstants.SQLITE_EXT); } else if (SLOPE_FILE == this) { @@ -160,7 +163,7 @@ public class DownloadActivityType { } return false; } - + public File getDownloadFolder(OsmandApplication ctx, IndexItem indexItem) { if (NORMAL_FILE == this) { if (indexItem.fileName.endsWith(IndexConstants.SQLITE_EXT)) { @@ -196,17 +199,17 @@ public class DownloadActivityType { } public boolean isZipStream(OsmandApplication ctx, IndexItem indexItem) { - return HILLSHADE_FILE != this && SLOPE_FILE != this && SQLITE_FILE != this && WIKIVOYAGE_FILE != this && GPX_FILE != this; + return HILLSHADE_FILE != this && SLOPE_FILE != this && SQLITE_FILE != this && WIKIVOYAGE_FILE != this && GPX_FILE != this; } public boolean isZipFolder(OsmandApplication ctx, IndexItem indexItem) { return this == VOICE_FILE; } - + public boolean preventMediaIndexing(OsmandApplication ctx, IndexItem indexItem) { return this == VOICE_FILE && indexItem.fileName.endsWith(IndexConstants.VOICE_INDEX_EXT_ZIP); } - + public String getUnzipExtension(OsmandApplication ctx, IndexItem indexItem) { if (NORMAL_FILE == this) { if (indexItem.fileName.endsWith(IndexConstants.BINARY_MAP_INDEX_EXT_ZIP)) { @@ -217,7 +220,7 @@ public class DownloadActivityType { return IndexConstants.EXTRA_EXT; } else if (indexItem.fileName.endsWith(IndexConstants.SQLITE_EXT)) { return IndexConstants.SQLITE_EXT; - } else if (indexItem.fileName.endsWith(IndexConstants.ANYVOICE_INDEX_EXT_ZIP)){ + } else if (indexItem.fileName.endsWith(IndexConstants.ANYVOICE_INDEX_EXT_ZIP)) { return ""; } } else if (ROADS_FILE == this) { @@ -227,7 +230,7 @@ public class DownloadActivityType { } else if (FONT_FILE == this) { return IndexConstants.FONT_INDEX_EXT; } else if (SRTM_COUNTRY_FILE == this) { - return IndexConstants.BINARY_SRTM_MAP_INDEX_EXT; + return SrtmDownloadItem.getExtension(indexItem); } else if (WIKIPEDIA_FILE == this) { return IndexConstants.BINARY_WIKI_MAP_INDEX_EXT; } else if (WIKIVOYAGE_FILE == this) { @@ -249,9 +252,9 @@ public class DownloadActivityType { } throw new UnsupportedOperationException(); } - + public String getUrlSuffix(OsmandApplication ctx) { - if (this== ROADS_FILE) { + if (this == ROADS_FILE) { return "&road=yes"; } else if (this == LIVE_UPDATES_FILE) { return "&aosmc=yes"; @@ -280,7 +283,7 @@ public class DownloadActivityType { public String getBaseUrl(OsmandApplication ctx, String fileName) { String url = "https://" + IndexConstants.INDEX_DOWNLOAD_DOMAIN + "/download?event=2&" + Version.getVersionAsURLParam(ctx) + "&file=" + encode(fileName); - if(this == LIVE_UPDATES_FILE && fileName.length() > 16) { + if (this == LIVE_UPDATES_FILE && fileName.length() > 16) { // DATE_AND_EXT_STR_LEN = "_18_06_02.obf.gz".length() String region = fileName.substring(0, fileName.length() - 16).toLowerCase(); url += "®ion=" + encode(region); @@ -343,7 +346,7 @@ public class DownloadActivityType { } return ""; } - + public String getVisibleName(DownloadItem downloadItem, Context ctx, OsmandRegions osmandRegions, boolean includingParent) { if (this == VOICE_FILE) { String fileName = downloadItem.getFileName(); @@ -361,6 +364,9 @@ public class DownloadActivityType { if (basename.endsWith(FileNameTranslationHelper.WIKI_NAME)) { return FileNameTranslationHelper.getWikiName(ctx, basename); } + if (basename.endsWith(FileNameTranslationHelper.WIKIVOYAGE_NAME)) { + return FileNameTranslationHelper.getWikivoyageName(ctx, basename); + } // if (this == HILLSHADE_FILE){ // return FileNameTranslationHelper.getHillShadeName(ctx, osmandRegions, bn); // } @@ -383,7 +389,7 @@ public class DownloadActivityType { return osmandRegions.getLocaleName(basename, includingParent); } - + public String getTargetFileName(IndexItem item) { String fileName = item.fileName; // if(fileName.endsWith(IndexConstants.VOICE_INDEX_EXT_ZIP) || @@ -423,7 +429,7 @@ public class DownloadActivityType { } String baseNameWithoutVersion = fileName.substring(0, l); if (this == SRTM_COUNTRY_FILE) { - return baseNameWithoutVersion + IndexConstants.BINARY_SRTM_MAP_INDEX_EXT; + return baseNameWithoutVersion + SrtmDownloadItem.getExtension(item); } if (this == WIKIPEDIA_FILE) { return baseNameWithoutVersion + IndexConstants.BINARY_WIKI_MAP_INDEX_EXT; @@ -487,7 +493,7 @@ public class DownloadActivityType { return fileName.substring(0, l); } if (this == LIVE_UPDATES_FILE) { - if(fileName.indexOf('.') > 0){ + if (fileName.indexOf('.') > 0) { return fileName.substring(0, fileName.indexOf('.')); } return fileName; @@ -495,10 +501,10 @@ public class DownloadActivityType { int ls = fileName.lastIndexOf('_'); if (ls >= 0) { return fileName.substring(0, ls); - } else if(fileName.indexOf('.') > 0){ + } else if (fileName.indexOf('.') > 0) { return fileName.substring(0, fileName.indexOf('.')); } return fileName; } -} \ No newline at end of file +} diff --git a/OsmAnd/src/net/osmand/plus/download/DownloadFileHelper.java b/OsmAnd/src/net/osmand/plus/download/DownloadFileHelper.java index c85d63361a..257e46db00 100644 --- a/OsmAnd/src/net/osmand/plus/download/DownloadFileHelper.java +++ b/OsmAnd/src/net/osmand/plus/download/DownloadFileHelper.java @@ -7,6 +7,7 @@ import net.osmand.osm.io.NetworkUtils; import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; import net.osmand.plus.Version; +import net.osmand.plus.download.IndexItem.DownloadEntry; import net.osmand.plus.helpers.FileNameTranslationHelper; import net.osmand.util.Algorithms; @@ -199,33 +200,35 @@ public class DownloadFileHelper { public boolean isWifiConnected(){ return ctx.getSettings().isWifiConnected(); } - - public boolean downloadFile(IndexItem.DownloadEntry de, IProgress progress, - List toReIndex, DownloadFileShowWarning showWarningCallback, boolean forceWifi) throws InterruptedException { + + public boolean downloadFile(IndexItem.DownloadEntry de, IProgress progress, + List toReIndex, DownloadFileShowWarning showWarningCallback, boolean forceWifi) throws InterruptedException { try { final List downloadInputStreams = new ArrayList(); URL url = new URL(de.urlToDownload); //$NON-NLS-1$ log.info("Url downloading " + de.urlToDownload); downloadInputStreams.add(getInputStreamToDownload(url, forceWifi)); de.fileToDownload = de.targetFile; - if(!de.unzipFolder) { - de.fileToDownload = new File(de.targetFile.getParentFile(), de.targetFile.getName() +".download"); + if (!de.unzipFolder) { + de.fileToDownload = new File(de.targetFile.getParentFile(), de.targetFile.getName() + ".download"); } unzipFile(de, progress, downloadInputStreams); - if(!de.targetFile.getAbsolutePath().equals(de.fileToDownload.getAbsolutePath())){ - boolean successfull = Algorithms.removeAllFiles(de.targetFile); - if (successfull) { + if (!de.targetFile.getAbsolutePath().equals(de.fileToDownload.getAbsolutePath())) { + boolean successful = Algorithms.removeAllFiles(de.targetFile); + if (successful) { ctx.getResourceManager().closeFile(de.targetFile.getName()); } - + boolean renamed = de.fileToDownload.renameTo(de.targetFile); - if(!renamed) { + if (!renamed) { showWarningCallback.showWarning(ctx.getString(R.string.shared_string_io_error) + ": old file can't be deleted"); return false; } } - if (de.type == DownloadActivityType.VOICE_FILE){ + if (de.type == DownloadActivityType.VOICE_FILE) { copyVoiceConfig(de); + } else if (de.type == DownloadActivityType.SRTM_COUNTRY_FILE) { + removePreviousSrtmFile(de); } toReIndex.add(de.targetFile); return true; @@ -238,6 +241,26 @@ public class DownloadFileHelper { } } + private void removePreviousSrtmFile(DownloadEntry entry) { + String meterExt = IndexConstants.BINARY_SRTM_MAP_INDEX_EXT; + String feetExt = IndexConstants.BINARY_SRTM_FEET_MAP_INDEX_EXT; + + String fileName = entry.targetFile.getAbsolutePath(); + if (fileName.endsWith(meterExt)) { + fileName = fileName.replace(meterExt, feetExt); + } else if (fileName.endsWith(feetExt)) { + fileName = fileName.replace(feetExt, meterExt); + } + + File previous = new File(fileName); + if (previous != null && previous.exists()) { + boolean successful = Algorithms.removeAllFiles(previous); + if (successful) { + ctx.getResourceManager().closeFile(previous.getName()); + } + } + } + private void copyVoiceConfig(IndexItem.DownloadEntry de) { File f = ctx.getAppPath("/voice/" + de.baseName + "/_config.p"); if (f.exists()) try { @@ -386,23 +409,20 @@ public class DownloadFileHelper { } return r; } - + @Override public int available() throws IOException { int av = 0; - for(int i = currentRead; i < delegate.length; i++) { + for (int i = currentRead; i < delegate.length; i++) { av += delegate[i].available(); } return av; } - + public int getAndClearReadCount() { int last = count; count = 0; return last; } - - - } } diff --git a/OsmAnd/src/net/osmand/plus/download/DownloadIndexesThread.java b/OsmAnd/src/net/osmand/plus/download/DownloadIndexesThread.java index 8b16e48d84..45d9b77402 100644 --- a/OsmAnd/src/net/osmand/plus/download/DownloadIndexesThread.java +++ b/OsmAnd/src/net/osmand/plus/download/DownloadIndexesThread.java @@ -9,7 +9,6 @@ import android.net.TrafficStats; import android.net.Uri; import android.os.AsyncTask; import android.os.AsyncTask.Status; -import android.os.StatFs; import android.view.View; import android.widget.Toast; @@ -17,13 +16,12 @@ import androidx.annotation.UiThread; import androidx.appcompat.app.AlertDialog; import net.osmand.AndroidNetworkUtils; +import net.osmand.AndroidUtils; import net.osmand.IndexConstants; import net.osmand.PlatformUtil; import net.osmand.map.WorldRegion; import net.osmand.map.WorldRegion.RegionParams; import net.osmand.plus.OsmandApplication; -import net.osmand.plus.settings.backend.OsmandSettings; -import net.osmand.plus.settings.backend.OsmandPreference; import net.osmand.plus.R; import net.osmand.plus.Version; import net.osmand.plus.base.BasicProgressAsyncTask; @@ -31,6 +29,8 @@ import net.osmand.plus.download.DownloadFileHelper.DownloadFileShowWarning; import net.osmand.plus.helpers.DatabaseHelper; import net.osmand.plus.notifications.OsmandNotification; import net.osmand.plus.resources.ResourceManager; +import net.osmand.plus.settings.backend.OsmandPreference; +import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.util.Algorithms; import org.apache.commons.logging.Log; @@ -240,9 +240,12 @@ public class DownloadIndexesThread { } public void cancelDownload(DownloadItem item) { - if (item instanceof MultipleIndexItem) { - MultipleIndexItem multipleIndexItem = (MultipleIndexItem) item; - cancelDownload(multipleIndexItem.getAllIndexes()); + if (item instanceof MultipleDownloadItem) { + MultipleDownloadItem multipleDownloadItem = (MultipleDownloadItem) item; + cancelDownload(multipleDownloadItem.getAllIndexes()); + } else if (item instanceof SrtmDownloadItem) { + IndexItem indexItem = ((SrtmDownloadItem) item).getIndexItem(); + cancelDownload(indexItem); } else if (item instanceof IndexItem) { IndexItem indexItem = (IndexItem) item; cancelDownload(indexItem); @@ -299,19 +302,8 @@ public class DownloadIndexesThread { return null; } - @SuppressWarnings("deprecation") public double getAvailableSpace() { - File dir = app.getAppPath("").getParentFile(); - double asz = -1; - if (dir.canRead()) { - try { - StatFs fs = new StatFs(dir.getAbsolutePath()); - asz = (((long) fs.getAvailableBlocks()) * fs.getBlockSize()) / (1 << 20); - } catch (IllegalArgumentException e) { - LOG.error(e); - } - } - return asz; + return AndroidUtils.getAvailableSpace(app) / (1 << 20); } /// PRIVATE IMPL diff --git a/OsmAnd/src/net/osmand/plus/download/DownloadItem.java b/OsmAnd/src/net/osmand/plus/download/DownloadItem.java index 366f3554a7..6b758f8af8 100644 --- a/OsmAnd/src/net/osmand/plus/download/DownloadItem.java +++ b/OsmAnd/src/net/osmand/plus/download/DownloadItem.java @@ -3,12 +3,14 @@ package net.osmand.plus.download; import android.content.Context; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import net.osmand.map.OsmandRegions; import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; import java.io.File; +import java.text.DateFormat; import java.util.List; import java.util.Locale; @@ -55,6 +57,12 @@ public abstract class DownloadItem { return type.getBasename(this); } + @NonNull + public abstract List getDownloadedFiles(@NonNull OsmandApplication app); + + @Nullable + public abstract String getAdditionalDescription(Context ctx); + protected abstract double getSizeToDownloadInMb(); public abstract double getArchiveSizeMB(); @@ -69,8 +77,7 @@ public abstract class DownloadItem { public abstract String getFileName(); - @NonNull - public abstract List getDownloadedFiles(@NonNull OsmandApplication app); + public abstract String getDate(@NonNull DateFormat dateFormat, boolean remote); @NonNull public static String getFormattedMb(@NonNull Context ctx, double sizeInMb) { diff --git a/OsmAnd/src/net/osmand/plus/download/DownloadOsmandIndexesHelper.java b/OsmAnd/src/net/osmand/plus/download/DownloadOsmandIndexesHelper.java index d77245cecb..a67de8b8d7 100644 --- a/OsmAnd/src/net/osmand/plus/download/DownloadOsmandIndexesHelper.java +++ b/OsmAnd/src/net/osmand/plus/download/DownloadOsmandIndexesHelper.java @@ -1,5 +1,26 @@ package net.osmand.plus.download; +import android.annotation.SuppressLint; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.res.AssetManager; +import android.provider.Settings.Secure; + +import net.osmand.AndroidUtils; +import net.osmand.IndexConstants; +import net.osmand.PlatformUtil; +import net.osmand.osm.io.NetworkUtils; +import net.osmand.plus.OsmandApplication; +import net.osmand.plus.settings.backend.OsmandSettings; +import net.osmand.util.Algorithms; + +import org.apache.commons.logging.Log; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlPullParserFactory; + import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -11,29 +32,9 @@ import java.util.Comparator; import java.util.List; import java.util.zip.GZIPInputStream; -import net.osmand.AndroidUtils; -import net.osmand.IndexConstants; -import net.osmand.PlatformUtil; -import net.osmand.osm.io.NetworkUtils; -import net.osmand.plus.OsmandApplication; -import net.osmand.plus.settings.backend.OsmandSettings; - -import org.apache.commons.logging.Log; -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlPullParserFactory; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; -import android.content.res.AssetManager; -import android.provider.Settings.Secure; - public class DownloadOsmandIndexesHelper { private final static Log log = PlatformUtil.getLog(DownloadOsmandIndexesHelper.class); - + public static class IndexFileList implements Serializable { private static final long serialVersionUID = 1L; @@ -41,29 +42,29 @@ public class DownloadOsmandIndexesHelper { IndexItem basemap; ArrayList indexFiles = new ArrayList(); private String mapversion; - - private Comparator comparator = new Comparator(){ + + private Comparator comparator = new Comparator() { @Override public int compare(IndexItem o1, IndexItem o2) { String object1 = o1.getFileName(); String object2 = o2.getFileName(); - if(object1.endsWith(IndexConstants.ANYVOICE_INDEX_EXT_ZIP)){ - if(object2.endsWith(IndexConstants.ANYVOICE_INDEX_EXT_ZIP)){ + if (object1.endsWith(IndexConstants.ANYVOICE_INDEX_EXT_ZIP)) { + if (object2.endsWith(IndexConstants.ANYVOICE_INDEX_EXT_ZIP)) { return object1.compareTo(object2); } else { return -1; } - } else if(object2.endsWith(IndexConstants.ANYVOICE_INDEX_EXT_ZIP)){ + } else if (object2.endsWith(IndexConstants.ANYVOICE_INDEX_EXT_ZIP)) { return 1; } return object1.compareTo(object2); } }; - + public void setDownloadedFromInternet(boolean downloadedFromInternet) { this.downloadedFromInternet = downloadedFromInternet; } - + public boolean isDownloadedFromInternet() { return downloadedFromInternet; } @@ -75,12 +76,12 @@ public class DownloadOsmandIndexesHelper { @SuppressLint("DefaultLocale") public void add(IndexItem indexItem) { indexFiles.add(indexItem); - if(indexItem.getFileName().toLowerCase().startsWith("world_basemap")) { + if (indexItem.getFileName().toLowerCase().startsWith("world_basemap")) { basemap = indexItem; } } - - public void sort(){ + + public void sort() { Collections.sort(indexFiles, comparator); } @@ -91,7 +92,7 @@ public class DownloadOsmandIndexesHelper { public List getIndexFiles() { return indexFiles; } - + public IndexItem getBasemap() { return basemap; } @@ -106,7 +107,7 @@ public class DownloadOsmandIndexesHelper { return false; } - } + } public static IndexFileList getIndexesList(OsmandApplication app) { PackageManager pm = app.getPackageManager(); @@ -141,16 +142,16 @@ public class DownloadOsmandIndexesHelper { } private static void listVoiceAssets(IndexFileList result, AssetManager amanager, PackageManager pm, - OsmandSettings settings) { + OsmandSettings settings) { try { - File voicePath = settings.getContext().getAppPath(IndexConstants.VOICE_INDEX_DIR); + File voicePath = settings.getContext().getAppPath(IndexConstants.VOICE_INDEX_DIR); // list = amanager.list("voice"); String date = ""; long dateModified = System.currentTimeMillis(); try { OsmandApplication app = settings.getContext(); ApplicationInfo appInfo = pm.getApplicationInfo(app.getPackageName(), 0); - dateModified = new File(appInfo.sourceDir).lastModified(); + dateModified = new File(appInfo.sourceDir).lastModified(); date = AndroidUtils.formatDate((Context) settings.getContext(), dateModified); } catch (NameNotFoundException e) { log.error(e); @@ -177,17 +178,17 @@ public class DownloadOsmandIndexesHelper { log.error("Error while loading tts files from assets", e); //$NON-NLS-1$ } } - - private static IndexFileList downloadIndexesListFromInternet(OsmandApplication ctx){ + + private static IndexFileList downloadIndexesListFromInternet(OsmandApplication ctx) { try { IndexFileList result = new IndexFileList(); log.debug("Start loading list of index files"); //$NON-NLS-1$ try { String strUrl = ctx.getAppCustomization().getIndexesUrl(); long nd = ctx.getAppInitializer().getFirstInstalledDays(); - if(nd > 0) { - strUrl += "&nd=" + nd; + if (nd > 0) { + strUrl += "&nd=" + nd; } strUrl += "&ns=" + ctx.getAppInitializer().getNumberOfStarts(); try { @@ -202,12 +203,12 @@ public class DownloadOsmandIndexesHelper { GZIPInputStream gzin = new GZIPInputStream(in); parser.setInput(gzin, "UTF-8"); //$NON-NLS-1$ int next; - while((next = parser.next()) != XmlPullParser.END_DOCUMENT) { + while ((next = parser.next()) != XmlPullParser.END_DOCUMENT) { if (next == XmlPullParser.START_TAG) { DownloadActivityType tp = DownloadActivityType.getIndexType(parser.getAttributeValue(null, "type")); if (tp != null) { IndexItem it = tp.parseIndexItem(ctx, parser); - if(it != null) { + if (it != null) { result.add(it); } } else if ("osmand_regions".equals(parser.getName())) { @@ -226,7 +227,7 @@ public class DownloadOsmandIndexesHelper { log.error("Error while loading indexes from repository", e); //$NON-NLS-1$ return null; } - + if (result.isAcceptable()) { return result; } else { @@ -239,19 +240,19 @@ public class DownloadOsmandIndexesHelper { } public static class AssetIndexItem extends IndexItem { - + private final String assetName; private final String destFile; private final long dateModified; public AssetIndexItem(String fileName, String description, String date, - long dateModified, String size, long sizeL, String assetName, String destFile, DownloadActivityType type) { + long dateModified, String size, long sizeL, String assetName, String destFile, DownloadActivityType type) { super(fileName, description, dateModified, size, sizeL, sizeL, type); this.dateModified = dateModified; this.assetName = assetName; this.destFile = destFile; } - + public long getDateModified() { return dateModified; } @@ -261,7 +262,7 @@ public class DownloadOsmandIndexesHelper { return new DownloadEntry(assetName, destFile, dateModified); } - public String getDestFile(){ + public String getDestFile() { return destFile; } } diff --git a/OsmAnd/src/net/osmand/plus/download/DownloadResources.java b/OsmAnd/src/net/osmand/plus/download/DownloadResources.java index 40f4a7488a..3966f68814 100644 --- a/OsmAnd/src/net/osmand/plus/download/DownloadResources.java +++ b/OsmAnd/src/net/osmand/plus/download/DownloadResources.java @@ -25,12 +25,11 @@ import java.io.InputStream; import java.text.DateFormat; import java.text.ParseException; import java.util.ArrayList; -import java.util.HashSet; +import java.util.Collections; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.Set; import static net.osmand.plus.download.DownloadResourceGroup.DownloadResourceGroupType.REGION_MAPS; @@ -117,6 +116,16 @@ public class DownloadResources extends DownloadResourceGroup { return res; } + @NonNull + public List getDownloadItems(WorldRegion region) { + DownloadResourceGroup group = getRegionMapsGroup(region); + if (group != null) { + return group.getIndividualDownloadItems(); + } + return Collections.emptyList(); + } + + @NonNull public List getIndexItems(WorldRegion region) { if (groupByRegion != null) { List res = groupByRegion.get(region); @@ -124,7 +133,7 @@ public class DownloadResources extends DownloadResourceGroup { return res; } } - return new LinkedList<>(); + return Collections.emptyList(); } public void updateLoadedFiles() { @@ -471,30 +480,60 @@ public class DownloadResources extends DownloadResourceGroup { addGroup(otherGroup); createHillshadeSRTMGroups(); - collectMultipleIndexesItems(); + replaceIndividualSrtmWithGroups(region); + createMultipleDownloadItems(region); trimEmptyGroups(); updateLoadedFiles(); return true; } - private void collectMultipleIndexesItems() { - collectMultipleIndexesItems(region); + private void replaceIndividualSrtmWithGroups(@NonNull WorldRegion region) { + DownloadResourceGroup group = getRegionMapsGroup(region); + if (group != null) { + boolean useMetersByDefault = SrtmDownloadItem.isUseMetricByDefault(app); + boolean listModified = false; + DownloadActivityType srtmType = DownloadActivityType.SRTM_COUNTRY_FILE; + List individualItems = group.getIndividualDownloadItems(); + if (isListContainsType(individualItems, srtmType)) { + List srtmIndexes = new ArrayList<>(); + for (DownloadItem item : individualItems) { + if (item.getType() == srtmType && item instanceof IndexItem) { + srtmIndexes.add((IndexItem) item); + } + } + if (srtmIndexes.size() > 1) { + individualItems.removeAll(srtmIndexes); + group.addItem(new SrtmDownloadItem(srtmIndexes, useMetersByDefault)); + } + listModified = true; + } + if (listModified) { + sortDownloadItems(individualItems); + } + } + + List subRegions = region.getSubregions(); + if (!Algorithms.isEmpty(subRegions)) { + for (WorldRegion subRegion : subRegions) { + replaceIndividualSrtmWithGroups(subRegion); + } + } } - private void collectMultipleIndexesItems(@NonNull WorldRegion region) { + private void createMultipleDownloadItems(@NonNull WorldRegion region) { List subRegions = region.getSubregions(); if (Algorithms.isEmpty(subRegions)) return; DownloadResourceGroup group = getRegionMapsGroup(region); if (group != null) { boolean listModified = false; - List indexesList = group.getIndividualResources(); - List regionsToCollect = removeDuplicateRegions(subRegions); + List downloadItems = group.getIndividualDownloadItems(); + List uniqueSubRegions = WorldRegion.removeDuplicates(subRegions); for (DownloadActivityType type : DownloadActivityType.values()) { - if (!doesListContainIndexWithType(indexesList, type)) { - List indexesFromSubRegions = collectIndexesOfType(regionsToCollect, type); - if (indexesFromSubRegions != null) { - group.addItem(new MultipleIndexItem(region, indexesFromSubRegions, type)); + if (!isListContainsType(downloadItems, type)) { + List itemsFromSubRegions = collectItemsOfType(uniqueSubRegions, type); + if (itemsFromSubRegions != null) { + group.addItem(new MultipleDownloadItem(region, itemsFromSubRegions, type)); listModified = true; } } @@ -504,7 +543,7 @@ public class DownloadResources extends DownloadResourceGroup { } } for (WorldRegion subRegion : subRegions) { - collectMultipleIndexesItems(subRegion); + createMultipleDownloadItems(subRegion); } } @@ -517,43 +556,21 @@ public class DownloadResources extends DownloadResourceGroup { } @Nullable - private List collectIndexesOfType(@NonNull List regions, - @NonNull DownloadActivityType type) { - List collectedIndexes = new ArrayList<>(); + private List collectItemsOfType(@NonNull List regions, + @NonNull DownloadActivityType type) { + List collectedItems = new ArrayList<>(); for (WorldRegion region : regions) { - List regionIndexes = getIndexItems(region); boolean found = false; - if (regionIndexes != null) { - for (IndexItem index : regionIndexes) { - if (index.getType() == type) { - found = true; - collectedIndexes.add(index); - break; - } + for (DownloadItem item : getDownloadItems(region)) { + if (item.getType() == type) { + found = true; + collectedItems.add(item); + break; } } if (!found) return null; } - return collectedIndexes; - } - - private List removeDuplicateRegions(List regions) { - Set duplicates = new HashSet<>(); - for (int i = 0; i < regions.size() - 1; i++) { - WorldRegion r1 = regions.get(i); - for (int j = i + 1; j < regions.size(); j++) { - WorldRegion r2 = regions.get(j); - if (r1.containsRegion(r2)) { - duplicates.add(r2); - } else if (r2.containsRegion(r1)) { - duplicates.add(r1); - } - } - } - for (WorldRegion region : duplicates) { - regions.remove(region); - } - return regions; + return collectedItems; } private void buildRegionsGroups(WorldRegion region, DownloadResourceGroup group) { @@ -680,11 +697,11 @@ public class DownloadResources extends DownloadResourceGroup { && isIndexItemDownloaded(downloadThread, type, downloadRegion.getSuperregion(), res); } - private boolean doesListContainIndexWithType(List indexItems, - DownloadActivityType type) { - if (indexItems != null) { - for (IndexItem indexItem : indexItems) { - if (indexItem.getType() == type) { + private boolean isListContainsType(List items, + DownloadActivityType type) { + if (items != null) { + for (DownloadItem item : items) { + if (item.getType() == type) { return true; } } diff --git a/OsmAnd/src/net/osmand/plus/download/IndexItem.java b/OsmAnd/src/net/osmand/plus/download/IndexItem.java index da0103fdb0..4f12d032dc 100644 --- a/OsmAnd/src/net/osmand/plus/download/IndexItem.java +++ b/OsmAnd/src/net/osmand/plus/download/IndexItem.java @@ -1,6 +1,9 @@ package net.osmand.plus.download; +import android.content.Context; + import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import net.osmand.IndexConstants; import net.osmand.PlatformUtil; @@ -19,6 +22,7 @@ import java.util.Date; import java.util.List; public class IndexItem extends DownloadItem implements Comparable { + private static final Log log = PlatformUtil.getLog(IndexItem.class); String description; @@ -225,6 +229,15 @@ public class IndexItem extends DownloadItem implements Comparable { public String getDate(java.text.DateFormat format) { return format.format(new Date(timestamp)); } + + @Nullable + @Override + public String getAdditionalDescription(Context ctx) { + if (getType() == DownloadActivityType.SRTM_COUNTRY_FILE) { + return SrtmDownloadItem.getAbbreviationInScopes(ctx, this); + } + return null; + } public static class DownloadEntry { public long dateModified; @@ -254,5 +267,4 @@ public class IndexItem extends DownloadItem implements Comparable { } } - } diff --git a/OsmAnd/src/net/osmand/plus/download/MultipleIndexItem.java b/OsmAnd/src/net/osmand/plus/download/MultipleDownloadItem.java similarity index 57% rename from OsmAnd/src/net/osmand/plus/download/MultipleIndexItem.java rename to OsmAnd/src/net/osmand/plus/download/MultipleDownloadItem.java index 5f5ff9c297..a9cfd1e1af 100644 --- a/OsmAnd/src/net/osmand/plus/download/MultipleIndexItem.java +++ b/OsmAnd/src/net/osmand/plus/download/MultipleDownloadItem.java @@ -1,32 +1,47 @@ package net.osmand.plus.download; +import android.content.Context; + import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import net.osmand.map.WorldRegion; import net.osmand.plus.OsmandApplication; import java.io.File; +import java.text.DateFormat; import java.util.ArrayList; import java.util.List; -public class MultipleIndexItem extends DownloadItem { +public class MultipleDownloadItem extends DownloadItem { - private final List items; + private final List items; - public MultipleIndexItem(@NonNull WorldRegion region, - @NonNull List items, - @NonNull DownloadActivityType type) { + public MultipleDownloadItem(@NonNull WorldRegion region, + @NonNull List items, + @NonNull DownloadActivityType type) { super(type); this.items = items; } public List getAllIndexes() { + List indexes = new ArrayList<>(); + for (DownloadItem item : items) { + IndexItem index = getIndexItem(item); + if (index != null) { + indexes.add(index); + } + } + return indexes; + } + + public List getAllItems() { return items; } @Override public boolean isOutdated() { - for (IndexItem item : items) { + for (DownloadItem item : items) { if (item.isOutdated()) { return true; } @@ -36,7 +51,7 @@ public class MultipleIndexItem extends DownloadItem { @Override public boolean isDownloaded() { - for (IndexItem item : items) { + for (DownloadItem item : items) { if (item.isDownloaded()) { return true; } @@ -46,8 +61,8 @@ public class MultipleIndexItem extends DownloadItem { @Override public boolean isDownloading(@NonNull DownloadIndexesThread thread) { - for (IndexItem item : items) { - if (thread.isDownloading(item)) { + for (DownloadItem item : items) { + if (item.isDownloading(thread)) { return true; } } @@ -78,31 +93,31 @@ public class MultipleIndexItem extends DownloadItem { @Override public List getDownloadedFiles(@NonNull OsmandApplication app) { List result = new ArrayList<>(); - for (IndexItem item : items) { + for (DownloadItem item : items) { result.addAll(item.getDownloadedFiles(app)); } return result; } - public List getIndexesToDownload() { - List indexesToDownload = new ArrayList<>(); - for (IndexItem item : items) { + public List getItemsToDownload() { + List itemsToDownload = new ArrayList<>(); + for (DownloadItem item : getAllItems()) { if (item.hasActualDataToDownload()) { - indexesToDownload.add(item); + itemsToDownload.add(item); } } - return indexesToDownload; + return itemsToDownload; } @Override public boolean hasActualDataToDownload() { - return getIndexesToDownload().size() > 0; + return getItemsToDownload().size() > 0; } @Override public double getSizeToDownloadInMb() { double totalSizeMb = 0.0d; - for (IndexItem item : items) { + for (DownloadItem item : items) { if (item.hasActualDataToDownload()) { totalSizeMb += item.getSizeToDownloadInMb(); } @@ -113,10 +128,30 @@ public class MultipleIndexItem extends DownloadItem { @Override public double getArchiveSizeMB() { double result = 0.0d; - for (IndexItem item : items) { + for (DownloadItem item : items) { result += item.getArchiveSizeMB(); } return result; } + @Nullable + public static IndexItem getIndexItem(@NonNull DownloadItem obj) { + if (obj instanceof IndexItem) { + return (IndexItem) obj; + } else if (obj instanceof SrtmDownloadItem) { + return ((SrtmDownloadItem) obj).getDefaultIndexItem(); + } + return null; + } + + @Nullable + @Override + public String getAdditionalDescription(Context ctx) { + return null; + } + + @Override + public String getDate(@NonNull DateFormat dateFormat, boolean remote) { + return ""; + } } diff --git a/OsmAnd/src/net/osmand/plus/download/MultipleIndexesUiHelper.java b/OsmAnd/src/net/osmand/plus/download/MultipleIndexesUiHelper.java deleted file mode 100644 index f84da0a74a..0000000000 --- a/OsmAnd/src/net/osmand/plus/download/MultipleIndexesUiHelper.java +++ /dev/null @@ -1,113 +0,0 @@ -package net.osmand.plus.download; - -import androidx.annotation.NonNull; -import androidx.appcompat.app.AppCompatActivity; - -import net.osmand.map.OsmandRegions; -import net.osmand.plus.OsmandApplication; -import net.osmand.plus.R; -import net.osmand.plus.base.SelectMultipleItemsBottomSheet; -import net.osmand.plus.base.SelectMultipleItemsBottomSheet.OnApplySelectionListener; -import net.osmand.plus.base.SelectMultipleItemsBottomSheet.SelectableItem; -import net.osmand.plus.base.SelectMultipleItemsBottomSheet.SelectionUpdateListener; - -import java.text.DateFormat; -import java.util.ArrayList; -import java.util.List; - -public class MultipleIndexesUiHelper { - - public static void showDialog(@NonNull MultipleIndexItem multipleIndexItem, - @NonNull AppCompatActivity activity, - @NonNull final OsmandApplication app, - @NonNull DateFormat dateFormat, - boolean showRemoteDate, - @NonNull final SelectItemsToDownloadListener listener) { - List indexesToDownload = getIndexesToDownload(multipleIndexItem); - List allItems = new ArrayList<>(); - List selectedItems = new ArrayList<>(); - OsmandRegions osmandRegions = app.getRegions(); - for (IndexItem indexItem : multipleIndexItem.getAllIndexes()) { - SelectableItem selectableItem = new SelectableItem(); - selectableItem.setTitle(indexItem.getVisibleName(app, osmandRegions, false)); - - String size = indexItem.getSizeDescription(app); - String date = indexItem.getDate(dateFormat, showRemoteDate); - String description = app.getString(R.string.ltr_or_rtl_combine_via_bold_point, size, date); - selectableItem.setDescription(description); - - selectableItem.setIconId(indexItem.getType().getIconResource()); - selectableItem.setObject(indexItem); - allItems.add(selectableItem); - - if (indexesToDownload.contains(indexItem)) { - selectedItems.add(selectableItem); - } - } - - final SelectMultipleItemsBottomSheet dialog = - SelectMultipleItemsBottomSheet.showInstance(activity, allItems, selectedItems, true); - - dialog.setSelectionUpdateListener(new SelectionUpdateListener() { - @Override - public void onSelectionUpdate() { - dialog.setTitle(app.getString(R.string.welmode_download_maps)); - String total = app.getString(R.string.shared_string_total); - double sizeToDownload = getDownloadSizeInMb(dialog.getSelectedItems()); - String size = DownloadItem.getFormattedMb(app, sizeToDownload); - String description = - app.getString(R.string.ltr_or_rtl_combine_via_colon, total, size); - dialog.setDescription(description); - String btnTitle = app.getString(R.string.shared_string_download); - if (sizeToDownload > 0) { - btnTitle = app.getString(R.string.ltr_or_rtl_combine_via_dash, btnTitle, size); - } - dialog.setConfirmButtonTitle(btnTitle); - } - }); - - dialog.setOnApplySelectionListener(new OnApplySelectionListener() { - @Override - public void onSelectionApplied(List selectedItems) { - List indexItems = new ArrayList<>(); - for (SelectableItem item : selectedItems) { - Object obj = item.getObject(); - if (obj instanceof IndexItem) { - indexItems.add((IndexItem) obj); - } - } - listener.onItemsToDownloadSelected(indexItems); - } - }); - } - - private static List getIndexesToDownload(MultipleIndexItem multipleIndexItem) { - if (multipleIndexItem.hasActualDataToDownload()) { - // download left regions - return multipleIndexItem.getIndexesToDownload(); - } else { - // download all regions again - return multipleIndexItem.getAllIndexes(); - } - } - - private static double getDownloadSizeInMb(@NonNull List selectableItems) { - List indexItems = new ArrayList<>(); - for (SelectableItem i : selectableItems) { - Object obj = i.getObject(); - if (obj instanceof IndexItem) { - indexItems.add((IndexItem) obj); - } - } - double totalSizeMb = 0.0d; - for (IndexItem item : indexItems) { - totalSizeMb += item.getSizeToDownloadInMb(); - } - return totalSizeMb; - } - - public interface SelectItemsToDownloadListener { - void onItemsToDownloadSelected(List items); - } - -} diff --git a/OsmAnd/src/net/osmand/plus/download/SelectIndexesHelper.java b/OsmAnd/src/net/osmand/plus/download/SelectIndexesHelper.java new file mode 100644 index 0000000000..a6af99212a --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/download/SelectIndexesHelper.java @@ -0,0 +1,330 @@ +package net.osmand.plus.download; + +import android.view.View; + +import androidx.annotation.NonNull; +import androidx.appcompat.app.AppCompatActivity; + +import net.osmand.plus.OsmandApplication; +import net.osmand.plus.R; +import net.osmand.plus.base.MultipleSelectionBottomSheet; +import net.osmand.plus.base.MultipleSelectionBottomSheet.SelectionUpdateListener; +import net.osmand.plus.base.ModeSelectionBottomSheet; +import net.osmand.plus.base.MultipleSelectionWithModeBottomSheet; +import net.osmand.plus.base.SelectionBottomSheet; +import net.osmand.plus.base.SelectionBottomSheet.OnApplySelectionListener; +import net.osmand.plus.base.SelectionBottomSheet.DialogStateListener; +import net.osmand.plus.base.SelectionBottomSheet.SelectableItem; +import net.osmand.plus.widgets.multistatetoggle.RadioItem; +import net.osmand.plus.widgets.multistatetoggle.RadioItem.OnRadioItemClickListener; +import net.osmand.plus.widgets.multistatetoggle.TextToggleButton.TextRadioItem; +import net.osmand.util.Algorithms; + +import java.text.DateFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import static net.osmand.plus.download.MultipleDownloadItem.getIndexItem; + +public class SelectIndexesHelper { + + private final OsmandApplication app; + private final AppCompatActivity activity; + + private final ItemsToDownloadSelectedListener listener; + private final DateFormat dateFormat; + private final boolean showRemoteDate; + private final List itemsToDownload; + private final DownloadItem downloadItem; + private final boolean useMetricByDefault; + + private SelectionBottomSheet dialog; + + private SelectIndexesHelper(@NonNull DownloadItem downloadItem, + @NonNull AppCompatActivity activity, + @NonNull DateFormat dateFormat, + boolean showRemoteDate, + @NonNull ItemsToDownloadSelectedListener listener) { + this.app = (OsmandApplication) activity.getApplicationContext(); + this.activity = activity; + this.dateFormat = dateFormat; + this.showRemoteDate = showRemoteDate; + this.listener = listener; + this.downloadItem = downloadItem; + this.itemsToDownload = getItemsToDownload(downloadItem); + this.useMetricByDefault = SrtmDownloadItem.isUseMetricByDefault(app); + } + + public static void showDialog(@NonNull DownloadItem di, + @NonNull AppCompatActivity a, + @NonNull DateFormat df, + boolean showRemoteDate, + @NonNull ItemsToDownloadSelectedListener l) { + + SelectIndexesHelper h = new SelectIndexesHelper(di, a, df, showRemoteDate, l); + if (di.getType() == DownloadActivityType.SRTM_COUNTRY_FILE) { + if (di instanceof MultipleDownloadItem) { + h.showSrtmMultipleSelectionDialog(); + } else { + h.showSrtmTypeSelectionDialog(); + } + } else if (di instanceof MultipleDownloadItem) { + h.showMultipleSelectionDialog(); + } + } + + private void showMultipleSelectionDialog() { + MultipleDownloadItem mdi = (MultipleDownloadItem) downloadItem; + List allItems = new ArrayList<>(); + List selectedItems = new ArrayList<>(); + + for (DownloadItem di : mdi.getAllItems()) { + SelectableItem si = createSelectableItem(di); + allItems.add(si); + if (itemsToDownload.contains(di)) { + selectedItems.add(si); + } + } + + MultipleSelectionBottomSheet msDialog = + MultipleSelectionBottomSheet.showInstance(activity, allItems, selectedItems, true); + this.dialog = msDialog; + + msDialog.setDialogStateListener(new DialogStateListener() { + @Override + public void onDialogCreated() { + dialog.setTitle(app.getString(R.string.welmode_download_maps)); + } + + @Override + public void onCloseDialog() { } + }); + + msDialog.setSelectionUpdateListener(new SelectionUpdateListener() { + @Override + public void onSelectionUpdate() { + updateSize(); + } + }); + + msDialog.setOnApplySelectionListener(getOnApplySelectionListener(listener)); + } + + private void showSrtmMultipleSelectionDialog() { + MultipleDownloadItem mdi = (MultipleDownloadItem) downloadItem; + List allItems = new ArrayList<>(); + List selectedItems = new ArrayList<>(); + + for (DownloadItem di : mdi.getAllItems()) { + SelectableItem si = createSrtmSelectableItem((SrtmDownloadItem) di); + allItems.add(si); + if (itemsToDownload.contains(di)) { + selectedItems.add(si); + } + } + + final RadioItem meterBtn = createSrtmRadioBtn(true); + final RadioItem feetBtn = createSrtmRadioBtn(false); + List radioItems = new ArrayList<>(); + radioItems.add(meterBtn); + radioItems.add(feetBtn); + + MultipleSelectionBottomSheet msDialog = MultipleSelectionWithModeBottomSheet.showInstance( + activity, allItems, selectedItems, radioItems, true); + this.dialog = msDialog; + + msDialog.setDialogStateListener(new DialogStateListener() { + @Override + public void onDialogCreated() { + dialog.setTitle(app.getString(R.string.welmode_download_maps)); + dialog.setSelectedMode(useMetricByDefault ? meterBtn : feetBtn); + dialog.setSecondaryDescription(app.getString(R.string.srtm_download_list_help_message)); + } + + @Override + public void onCloseDialog() { + resetUseMeters(); + } + }); + + msDialog.setSelectionUpdateListener(new SelectionUpdateListener() { + @Override + public void onSelectionUpdate() { + updateSize(); + } + }); + + msDialog.setOnApplySelectionListener(getOnApplySelectionListener(listener)); + } + + private void showSrtmTypeSelectionDialog() { + SrtmDownloadItem srtmItem = (SrtmDownloadItem) downloadItem; + + final RadioItem meterBtn = createSrtmRadioBtn(true); + final RadioItem feetBtn = createSrtmRadioBtn(false); + List radioItems = new ArrayList<>(); + radioItems.add(meterBtn); + radioItems.add(feetBtn); + + SelectableItem preview = createSrtmSelectableItem(srtmItem); + + dialog = ModeSelectionBottomSheet.showInstance(activity, preview, radioItems, true); + + dialog.setDialogStateListener(new DialogStateListener() { + @Override + public void onDialogCreated() { + ModeSelectionBottomSheet dialog = (ModeSelectionBottomSheet) SelectIndexesHelper.this.dialog; + dialog.setTitle(app.getString(R.string.srtm_unit_format)); + dialog.setPrimaryDescription(app.getString(R.string.srtm_download_single_help_message)); + updateSize(); + dialog.setSelectedMode(useMetricByDefault ? meterBtn : feetBtn); + } + + @Override + public void onCloseDialog() { + resetUseMeters(); + } + }); + + dialog.setOnApplySelectionListener(getOnApplySelectionListener(listener)); + } + + private RadioItem createSrtmRadioBtn(final boolean useMeters) { + int titleId = useMeters ? R.string.shared_string_meters : R.string.shared_string_feet; + String title = Algorithms.capitalizeFirstLetter(app.getString(titleId)); + RadioItem radioItem = new TextRadioItem(title); + radioItem.setOnClickListener(new OnRadioItemClickListener() { + @Override + public boolean onRadioItemClick(RadioItem radioItem, View view) { + setUseMetersForAllItems(useMeters); + updateListItems(); + updateSize(); + return true; + } + }); + return radioItem; + } + + private SelectableItem createSelectableItem(DownloadItem item) { + SelectableItem selectableItem = new SelectableItem(); + updateSelectableItem(selectableItem, item); + selectableItem.setObject(item); + return selectableItem; + } + + private SelectableItem createSrtmSelectableItem(SrtmDownloadItem item) { + SelectableItem selectableItem = new SelectableItem(); + updateSelectableItem(selectableItem, item.getDefaultIndexItem()); + selectableItem.setObject(item); + return selectableItem; + } + + private void updateListItems() { + List items = new ArrayList<>(dialog.getAllItems()); + for (SelectableItem selectableItem : items) { + DownloadItem di = (DownloadItem) selectableItem.getObject(); + if (di instanceof SrtmDownloadItem) { + di = ((SrtmDownloadItem) di).getDefaultIndexItem(); + } + updateSelectableItem(selectableItem, di); + } + dialog.setItems(items); + } + + private void resetUseMeters() { + boolean useMeters = SrtmDownloadItem.isUseMetricByDefault(app); + setUseMetersForAllItems(useMeters); + } + + private void setUseMetersForAllItems(boolean useMeters) { + for (SelectableItem item : dialog.getAllItems()) { + DownloadItem downloadItem = (DownloadItem) item.getObject(); + if (downloadItem instanceof SrtmDownloadItem) { + SrtmDownloadItem srtmItem = (SrtmDownloadItem) downloadItem; + srtmItem.setUseMetric(useMeters); + } + } + } + + private void updateSelectableItem(SelectableItem selectableItem, + DownloadItem downloadItem) { + selectableItem.setTitle(downloadItem.getVisibleName(app, app.getRegions(), false)); + + String size = downloadItem.getSizeDescription(app); + String addDescr = downloadItem.getAdditionalDescription(app); + if (addDescr != null) { + size += " " + addDescr; + } + String date = downloadItem.getDate(dateFormat, showRemoteDate); + String description = app.getString(R.string.ltr_or_rtl_combine_via_bold_point, size, date); + selectableItem.setDescription(description); + + selectableItem.setIconId(downloadItem.getType().getIconResource()); + } + + private OnApplySelectionListener getOnApplySelectionListener(final ItemsToDownloadSelectedListener listener) { + return new OnApplySelectionListener() { + @Override + public void onSelectionApplied(List selectedItems) { + List indexes = new ArrayList<>(); + for (SelectableItem item : selectedItems) { + IndexItem index = getIndexItem((DownloadItem) item.getObject()); + if (index != null) { + indexes.add(index); + } + } + listener.onItemsToDownloadSelected(indexes); + } + }; + } + + private void updateSize() { + double sizeToDownload = getDownloadSizeInMb(dialog.getSelectedItems()); + String size = DownloadItem.getFormattedMb(app, sizeToDownload); + String total = app.getString(R.string.shared_string_total); + String description = app.getString(R.string.ltr_or_rtl_combine_via_colon, total, size); + dialog.setTitleDescription(description); + String btnTitle = app.getString(R.string.shared_string_download); + if (sizeToDownload > 0) { + btnTitle = app.getString(R.string.ltr_or_rtl_combine_via_dash, btnTitle, size); + } + dialog.setApplyButtonTitle(btnTitle); + } + + private double getDownloadSizeInMb(@NonNull List selectableItems) { + double totalSizeMb = 0.0d; + for (SelectableItem i : selectableItems) { + Object obj = i.getObject(); + if (obj instanceof SrtmDownloadItem) { + SrtmDownloadItem srtm = (SrtmDownloadItem) obj; + totalSizeMb += srtm.getDefaultIndexItem().getSizeToDownloadInMb(); + } else if (obj instanceof DownloadItem) { + totalSizeMb += ((DownloadItem) obj).getSizeToDownloadInMb(); + } + } + return totalSizeMb; + } + + private static List getItemsToDownload(DownloadItem di) { + if (di instanceof MultipleDownloadItem) { + return getItemsToDownload((MultipleDownloadItem) di); + } + return Collections.emptyList(); + } + + private static List getItemsToDownload(MultipleDownloadItem md) { + if (md.hasActualDataToDownload()) { + // download left regions + return md.getItemsToDownload(); + } else { + // download all regions again + return md.getAllItems(); + } + } + + public interface ItemsToDownloadSelectedListener { + void onItemsToDownloadSelected(List items); + } + +} diff --git a/OsmAnd/src/net/osmand/plus/download/SrtmDownloadItem.java b/OsmAnd/src/net/osmand/plus/download/SrtmDownloadItem.java new file mode 100644 index 0000000000..217b9eb0ee --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/download/SrtmDownloadItem.java @@ -0,0 +1,196 @@ +package net.osmand.plus.download; + +import android.content.Context; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import net.osmand.IndexConstants; +import net.osmand.plus.OsmandApplication; +import net.osmand.plus.R; +import net.osmand.plus.activities.LocalIndexInfo; +import net.osmand.plus.helpers.enums.MetricsConstants; +import net.osmand.util.Algorithms; + +import java.io.File; +import java.text.DateFormat; +import java.util.ArrayList; +import java.util.List; + +import static net.osmand.IndexConstants.BINARY_SRTM_MAP_INDEX_EXT; +import static net.osmand.IndexConstants.BINARY_SRTM_MAP_INDEX_EXT_ZIP; +import static net.osmand.plus.activities.LocalIndexHelper.LocalIndexType.SRTM_DATA; +import static net.osmand.plus.download.DownloadActivityType.SRTM_COUNTRY_FILE; + +public class SrtmDownloadItem extends DownloadItem { + + private final List indexes; + private boolean useMetric; + + public SrtmDownloadItem(List indexes, boolean useMetric) { + super(SRTM_COUNTRY_FILE); + this.indexes = indexes; + this.useMetric = useMetric; + } + + public void setUseMetric(boolean useMetric) { + this.useMetric = useMetric; + } + + public boolean isUseMetric() { + for (IndexItem index : indexes) { + if (index.isDownloaded()) { + return isMetricItem(index); + } + } + return useMetric; + } + + @NonNull + public IndexItem getIndexItem() { + for (IndexItem index : indexes) { + if (index.isDownloaded()) { + return index; + } + } + return getDefaultIndexItem(); + } + + @NonNull + public IndexItem getDefaultIndexItem() { + for (IndexItem index : indexes) { + if (useMetric && isMetricItem(index) || !useMetric && !isMetricItem(index)) { + return index; + } + } + return indexes.get(0); + } + + @Override + protected double getSizeToDownloadInMb() { + return getIndexItem().getSizeToDownloadInMb(); + } + + @Override + public double getArchiveSizeMB() { + return getIndexItem().getArchiveSizeMB(); + } + + @Override + public boolean isOutdated() { + for (DownloadItem item : indexes) { + if (item.isOutdated()) { + return true; + } + } + return false; + } + + @Override + public boolean isDownloaded() { + for (DownloadItem item : indexes) { + if (item.isDownloaded()) { + return true; + } + } + return false; + } + + @Override + public boolean hasActualDataToDownload() { + for (IndexItem item : indexes) { + if (!item.hasActualDataToDownload()) { + return false; + } + } + return true; + } + + @Override + public boolean isDownloading(@NonNull DownloadIndexesThread thread) { + for (IndexItem item : indexes) { + if (thread.isDownloading(item)) { + return true; + } + } + return false; + } + + @Override + public String getFileName() { + return getIndexItem().getFileName(); + } + + @NonNull + @Override + public List getDownloadedFiles(@NonNull OsmandApplication app) { + List result = new ArrayList<>(); + for (IndexItem index : indexes) { + result.addAll(index.getDownloadedFiles(app)); + } + return result; + } + + public String getDate(@NonNull DateFormat dateFormat, boolean remote) { + return getIndexItem().getDate(dateFormat, remote); + } + + @Override + public @Nullable String getAdditionalDescription(Context ctx) { + return getAbbreviationInScopes(ctx, this); + } + + public static boolean isUseMetricByDefault(@NonNull OsmandApplication app) { + MetricsConstants metricSystem = app.getSettings().METRIC_SYSTEM.get(); + return metricSystem != MetricsConstants.MILES_AND_FEET; + } + + @NonNull + public static String getAbbreviationInScopes(Context ctx, Object obj) { + String abbreviation = ctx.getString(isMetricItem(obj) ? R.string.m : R.string.foot); + return "(" + abbreviation + ")"; + } + + public static boolean containsSrtmExtension(@NonNull String fileName) { + return fileName.contains(IndexConstants.BINARY_SRTM_MAP_INDEX_EXT) + || fileName.contains(IndexConstants.BINARY_SRTM_FEET_MAP_INDEX_EXT); + } + + public static boolean isSrtmFile(@NonNull String fileName) { + return fileName.endsWith(IndexConstants.BINARY_SRTM_MAP_INDEX_EXT) + || fileName.endsWith(IndexConstants.BINARY_SRTM_FEET_MAP_INDEX_EXT); + } + + @NonNull + public static String getExtension(IndexItem indexItem) { + return isMetricItem(indexItem) ? + IndexConstants.BINARY_SRTM_MAP_INDEX_EXT : + IndexConstants.BINARY_SRTM_FEET_MAP_INDEX_EXT; + } + + public static boolean isSRTMItem(Object item) { + if (item instanceof DownloadItem) { + return ((DownloadItem) item).getType() == SRTM_COUNTRY_FILE; + } else if (item instanceof LocalIndexInfo) { + return ((LocalIndexInfo) item).getType() == SRTM_DATA; + } + return false; + } + + private static boolean isMetricItem(Object item) { + if (item instanceof IndexItem) { + return ((IndexItem) item).getFileName().endsWith(BINARY_SRTM_MAP_INDEX_EXT_ZIP); + } else if (item instanceof LocalIndexInfo) { + return ((LocalIndexInfo) item).getFileName().endsWith(BINARY_SRTM_MAP_INDEX_EXT); + } else if (item instanceof SrtmDownloadItem) { + return isMetricItem(((SrtmDownloadItem) item).getIndexItem()); + } else if (item instanceof MultipleDownloadItem) { + List items = ((MultipleDownloadItem) item).getAllItems(); + if (!Algorithms.isEmpty(items)) { + return isMetricItem(items.get(0)); + } + } + return false; + } + +} diff --git a/OsmAnd/src/net/osmand/plus/download/ui/ActiveDownloadsDialogFragment.java b/OsmAnd/src/net/osmand/plus/download/ui/ActiveDownloadsDialogFragment.java index 1b8667685c..31a0205f0e 100644 --- a/OsmAnd/src/net/osmand/plus/download/ui/ActiveDownloadsDialogFragment.java +++ b/OsmAnd/src/net/osmand/plus/download/ui/ActiveDownloadsDialogFragment.java @@ -87,7 +87,7 @@ public class ActiveDownloadsDialogFragment extends DialogFragment implements Dow } ItemViewHolder viewHolder = (ItemViewHolder) convertView.getTag(); IndexItem item = getItem(position); - viewHolder.bindIndexItem(item); + viewHolder.bindDownloadItem(item); return convertView; } diff --git a/OsmAnd/src/net/osmand/plus/download/ui/DataStoragePlaceDialogFragment.java b/OsmAnd/src/net/osmand/plus/download/ui/DataStoragePlaceDialogFragment.java index be619f324f..76bc14f5af 100644 --- a/OsmAnd/src/net/osmand/plus/download/ui/DataStoragePlaceDialogFragment.java +++ b/OsmAnd/src/net/osmand/plus/download/ui/DataStoragePlaceDialogFragment.java @@ -5,7 +5,6 @@ import android.content.DialogInterface; import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; -import android.os.StatFs; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; @@ -17,8 +16,6 @@ import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; -import com.ibm.icu.impl.IllegalIcuArgumentException; - import androidx.annotation.NonNull; import androidx.fragment.app.FragmentManager; @@ -28,12 +25,12 @@ import net.osmand.IProgress; import net.osmand.PlatformUtil; import net.osmand.plus.OnDismissDialogFragmentListener; import net.osmand.plus.OsmandApplication; -import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.R; import net.osmand.plus.base.BottomSheetDialogFragment; import net.osmand.plus.dashboard.DashChooseAppDirFragment; import net.osmand.plus.download.DownloadActivity; import net.osmand.plus.download.DownloadIndexesThread; +import net.osmand.plus.settings.backend.OsmandSettings; import org.apache.commons.logging.Log; @@ -119,7 +116,7 @@ public class DataStoragePlaceDialogFragment extends BottomSheetDialogFragment { deviceStorageImageView.setImageDrawable(getContentIcon(R.drawable.ic_action_phone)); TextView deviceStorageDescription = (TextView) view.findViewById(R.id.deviceMemoryDescription); deviceStorageDescription.setText(deviceStorageName); - deviceStorageDescription.setText(getFreeSpace(deviceStorage)); + deviceStorageDescription.setText(AndroidUtils.getFreeSpace(activity, deviceStorage)); View sharedMemoryRow = view.findViewById(R.id.sharedMemoryRow); if (hasExternalStoragePermission && sharedStorage != null) { @@ -127,7 +124,7 @@ public class DataStoragePlaceDialogFragment extends BottomSheetDialogFragment { ImageView sharedMemoryImageView = (ImageView) view.findViewById(R.id.sharedMemoryImageView); sharedMemoryImageView.setImageDrawable(getContentIcon(R.drawable.ic_action_phone)); TextView sharedMemoryDescription = (TextView) view.findViewById(R.id.sharedMemoryDescription); - sharedMemoryDescription.setText(getFreeSpace(sharedStorage)); + sharedMemoryDescription.setText(AndroidUtils.getFreeSpace(activity, sharedStorage)); } else { view.findViewById(R.id.divSharedStorage).setVisibility(View.GONE); sharedMemoryRow.setVisibility(View.GONE); @@ -139,7 +136,7 @@ public class DataStoragePlaceDialogFragment extends BottomSheetDialogFragment { ImageView memoryStickImageView = (ImageView) view.findViewById(R.id.memoryStickImageView); memoryStickImageView.setImageDrawable(getContentIcon(R.drawable.ic_sdcard)); TextView memoryStickDescription = (TextView) view.findViewById(R.id.memoryStickDescription); - memoryStickDescription.setText(getFreeSpace(cardStorage)); + memoryStickDescription.setText(AndroidUtils.getFreeSpace(activity, cardStorage)); } else { view.findViewById(R.id.divExtStorage).setVisibility(View.GONE); memoryStickRow.setVisibility(View.GONE); @@ -192,23 +189,6 @@ public class DataStoragePlaceDialogFragment extends BottomSheetDialogFragment { .getDefaultInternalStorage(); } - private String getFreeSpace(File dir) { - String sz = ""; - if (dir != null && dir.canRead()) { - try { - StatFs fs = new StatFs(dir.getAbsolutePath()); - @SuppressWarnings("deprecation") - long size = (long) fs.getAvailableBlocks() * fs.getBlockSize(); - if (size > 0) { - sz = AndroidUtils.formatSize(getActivity(), size); - } - } catch (IllegalIcuArgumentException e) { - LOG.error(e); - } - } - return sz; - } - private void checkAssets() { getMyApplication().getResourceManager().checkAssets(IProgress.EMPTY_PROGRESS, true); } diff --git a/OsmAnd/src/net/osmand/plus/download/ui/DownloadResourceGroupAdapter.java b/OsmAnd/src/net/osmand/plus/download/ui/DownloadResourceGroupAdapter.java index d2e281bbfc..168888434d 100644 --- a/OsmAnd/src/net/osmand/plus/download/ui/DownloadResourceGroupAdapter.java +++ b/OsmAnd/src/net/osmand/plus/download/ui/DownloadResourceGroupAdapter.java @@ -78,7 +78,7 @@ public class DownloadResourceGroupAdapter extends OsmandBaseExpandableListAdapte } else { viewHolder.setShowTypeInDesc(true); } - viewHolder.bindIndexItem(item); + viewHolder.bindDownloadItem(item); } else { DownloadResourceGroup group = (DownloadResourceGroup) child; DownloadGroupViewHolder viewHolder; diff --git a/OsmAnd/src/net/osmand/plus/download/ui/ItemViewHolder.java b/OsmAnd/src/net/osmand/plus/download/ui/ItemViewHolder.java index a24e2c5b2a..ec4347eb71 100644 --- a/OsmAnd/src/net/osmand/plus/download/ui/ItemViewHolder.java +++ b/OsmAnd/src/net/osmand/plus/download/ui/ItemViewHolder.java @@ -41,9 +41,9 @@ import net.osmand.plus.download.DownloadActivityType; import net.osmand.plus.download.DownloadResourceGroup; import net.osmand.plus.download.DownloadResources; import net.osmand.plus.download.IndexItem; -import net.osmand.plus.download.MultipleIndexesUiHelper; -import net.osmand.plus.download.MultipleIndexesUiHelper.SelectItemsToDownloadListener; -import net.osmand.plus.download.MultipleIndexItem; +import net.osmand.plus.download.SelectIndexesHelper; +import net.osmand.plus.download.SelectIndexesHelper.ItemsToDownloadSelectedListener; +import net.osmand.plus.download.MultipleDownloadItem; import net.osmand.plus.download.ui.LocalIndexesFragment.LocalIndexOperationTask; import net.osmand.plus.helpers.FileNameTranslationHelper; import net.osmand.plus.inapp.InAppPurchaseHelper; @@ -146,11 +146,11 @@ public class ItemViewHolder { depthContoursPurchased = InAppPurchaseHelper.isDepthContoursPurchased(context.getMyApplication()); } - public void bindIndexItem(final DownloadItem downloadItem) { - bindIndexItem(downloadItem, null); + public void bindDownloadItem(final DownloadItem downloadItem) { + bindDownloadItem(downloadItem, null); } - public void bindIndexItem(final DownloadItem downloadItem, final String cityName) { + public void bindDownloadItem(final DownloadItem downloadItem, final String cityName) { initAppStatusVariables(); boolean isDownloading = downloadItem.isDownloading(context.getDownloadThread()); int progress = -1; @@ -160,20 +160,20 @@ public class ItemViewHolder { boolean disabled = checkDisabledAndClickAction(downloadItem); /// name and left item String name; - if(showTypeInName) { + if (showTypeInName) { name = downloadItem.getType().getString(context); } else { name = downloadItem.getVisibleName(context, context.getMyApplication().getRegions(), showParentRegionName); } String text = (!Algorithms.isEmpty(cityName) && !cityName.equals(name) ? cityName + "\n" : "") + name; nameTextView.setText(text); - if(!disabled) { + if (!disabled) { nameTextView.setTextColor(textColorPrimary); } else { nameTextView.setTextColor(textColorSecondary); } int color = textColorSecondary; - if(downloadItem.isDownloaded() && !isDownloading) { + if (downloadItem.isDownloaded() && !isDownloading) { int colorId = downloadItem.isOutdated() ? R.color.color_distance : R.color.color_ok; color = context.getResources().getColor(colorId); } @@ -203,12 +203,12 @@ public class ItemViewHolder { } else { descrTextView.setText(downloadItem.getType().getString(context)); } - } else if (downloadItem instanceof MultipleIndexItem) { - MultipleIndexItem item = (MultipleIndexItem) downloadItem; + } else if (downloadItem instanceof MultipleDownloadItem) { + MultipleDownloadItem item = (MultipleDownloadItem) downloadItem; String allRegionsHeader = context.getString(R.string.shared_strings_all_regions); String regionsHeader = context.getString(R.string.regions); - String allRegionsCount = String.valueOf(item.getAllIndexes().size()); - String leftToDownloadCount = String.valueOf(item.getIndexesToDownload().size()); + String allRegionsCount = String.valueOf(item.getAllItems().size()); + String leftToDownloadCount = String.valueOf(item.getItemsToDownload().size()); String header; String count; if (item.hasActualDataToDownload()) { @@ -226,8 +226,11 @@ public class ItemViewHolder { header = allRegionsHeader; count = allRegionsCount; } - String fullDescription = - context.getString(R.string.ltr_or_rtl_combine_via_colon, header, count); + String fullDescription = context.getString(R.string.ltr_or_rtl_combine_via_colon, header, count); + String addDescr = item.getAdditionalDescription(context); + if (addDescr != null) { + fullDescription += " " + addDescr; + } if (item.hasActualDataToDownload()) { fullDescription = context.getString( R.string.ltr_or_rtl_combine_via_bold_point, fullDescription, @@ -235,11 +238,14 @@ public class ItemViewHolder { } descrTextView.setText(fullDescription); } else { - IndexItem item = (IndexItem) downloadItem; String pattern = context.getString(R.string.ltr_or_rtl_combine_via_bold_point); - String type = item.getType().getString(context); - String size = item.getSizeDescription(context); - String date = item.getDate(dateFormat, showRemoteDate); + String type = downloadItem.getType().getString(context); + String size = downloadItem.getSizeDescription(context); + String addDescr = downloadItem.getAdditionalDescription(context); + if (addDescr != null) { + size += " " + addDescr; + } + String date = downloadItem.getDate(dateFormat, showRemoteDate); String fullDescription = String.format(pattern, size, date); if (showTypeInDesc) { fullDescription = String.format(pattern, type, fullDescription); @@ -254,14 +260,14 @@ public class ItemViewHolder { if (showProgressInDesc) { double mb = downloadItem.getArchiveSizeMB(); - String v ; + String v; if (progress != -1) { v = context.getString(R.string.value_downloaded_of_max, mb * progress / 100, mb); } else { v = context.getString(R.string.file_size_in_mb, mb); } String fullDescription = v; - if(showTypeInDesc && downloadItem.getType() == DownloadActivityType.ROADS_FILE) { + if (showTypeInDesc && downloadItem.getType() == DownloadActivityType.ROADS_FILE) { fullDescription = context.getString(R.string.ltr_or_rtl_combine_via_bold_point, downloadItem.getType().getString(context), fullDescription); } @@ -274,9 +280,9 @@ public class ItemViewHolder { } } - public void bindIndexItem(final CityItem cityItem) { + public void bindDownloadItem(final CityItem cityItem) { if (cityItem.getIndexItem() != null) { - bindIndexItem(cityItem.getIndexItem(), cityItem.getName()); + bindDownloadItem(cityItem.getIndexItem(), cityItem.getName()); } else { nameTextView.setText(cityItem.getName()); nameTextView.setTextColor(textColorPrimary); @@ -302,7 +308,7 @@ public class ItemViewHolder { if (isDownloading) { rightImageButton.setImageDrawable(getContentIcon(context, R.drawable.ic_action_remove_dark)); rightImageButton.setContentDescription(context.getString(R.string.shared_string_cancel)); - } else if(!item.hasActualDataToDownload()) { + } else if (!item.hasActualDataToDownload()) { rightImageButton.setImageDrawable(getContentIcon(context, R.drawable.ic_overflow_menu_white)); rightImageButton.setContentDescription(context.getString(R.string.shared_string_more)); } else { @@ -316,9 +322,9 @@ public class ItemViewHolder { } private int getDownloadActionIconId(@NonNull DownloadItem item) { - return item instanceof MultipleIndexItem ? + return item instanceof MultipleDownloadItem ? R.drawable.ic_action_multi_download : - R.drawable.ic_action_import; + R.drawable.ic_action_gsave_dark; } @SuppressLint("DefaultLocale") @@ -389,13 +395,13 @@ public class ItemViewHolder { return new View.OnClickListener() { @Override public void onClick(View v) { - if(isDownloading) { - if(silentCancelDownload) { + if (isDownloading) { + if (silentCancelDownload) { context.getDownloadThread().cancelDownload(item); } else { context.makeSureUserCancelDownload(item); } - } else if(!item.hasActualDataToDownload()){ + } else if (!item.hasActualDataToDownload()) { showContextMenu(v, item, item.getRelatedGroup()); } else { download(item, item.getRelatedGroup()); @@ -406,8 +412,8 @@ public class ItemViewHolder { } protected void showContextMenu(View v, - final DownloadItem downloadItem, - final DownloadResourceGroup parentOptional) { + final DownloadItem downloadItem, + final DownloadResourceGroup parentOptional) { OsmandApplication app = context.getMyApplication(); PopupMenu optionsMenu = new PopupMenu(context, v); MenuItem item; @@ -455,10 +461,11 @@ public class ItemViewHolder { } } } - if(!handled) { + if (!handled) { startDownload(item); } } + private void confirmDownload(final DownloadItem item) { AlertDialog.Builder builder = new AlertDialog.Builder(context); builder.setTitle(R.string.are_you_sure); @@ -476,18 +483,17 @@ public class ItemViewHolder { } private void startDownload(DownloadItem item) { - if (item instanceof MultipleIndexItem) { - selectIndexesToDownload((MultipleIndexItem) item); - } else if (item instanceof IndexItem) { + if (item instanceof IndexItem) { IndexItem indexItem = (IndexItem) item; context.startDownload(indexItem); + } else { + selectIndexesToDownload(item); } } - private void selectIndexesToDownload(MultipleIndexItem item) { - OsmandApplication app = context.getMyApplication(); - MultipleIndexesUiHelper.showDialog(item, context, app, dateFormat, showRemoteDate, - new SelectItemsToDownloadListener() { + private void selectIndexesToDownload(DownloadItem item) { + SelectIndexesHelper.showDialog(item, context, dateFormat, showRemoteDate, + new ItemsToDownloadSelectedListener() { @Override public void onItemsToDownloadSelected(List indexes) { IndexItem[] indexesArray = new IndexItem[indexes.size()]; @@ -498,7 +504,7 @@ public class ItemViewHolder { } private void confirmRemove(@NonNull final DownloadItem downloadItem, - @NonNull final List downloadedFiles) { + @NonNull final List downloadedFiles) { OsmandApplication app = context.getMyApplication(); AlertDialog.Builder confirm = new AlertDialog.Builder(context); @@ -526,7 +532,7 @@ public class ItemViewHolder { } private void remove(@NonNull LocalIndexType type, - @NonNull List filesToDelete) { + @NonNull List filesToDelete) { OsmandApplication app = context.getMyApplication(); LocalIndexOperationTask removeTask = new LocalIndexOperationTask( context, diff --git a/OsmAnd/src/net/osmand/plus/download/ui/LocalIndexesFragment.java b/OsmAnd/src/net/osmand/plus/download/ui/LocalIndexesFragment.java index 3b259ab241..2c691a78de 100644 --- a/OsmAnd/src/net/osmand/plus/download/ui/LocalIndexesFragment.java +++ b/OsmAnd/src/net/osmand/plus/download/ui/LocalIndexesFragment.java @@ -55,6 +55,7 @@ import net.osmand.plus.dialogs.DirectionsDialogs; import net.osmand.plus.download.DownloadActivity; import net.osmand.plus.download.DownloadIndexesThread.DownloadEvents; import net.osmand.plus.download.IndexItem; +import net.osmand.plus.download.SrtmDownloadItem; import net.osmand.plus.helpers.FileNameTranslationHelper; import net.osmand.plus.inapp.InAppPurchaseHelper; import net.osmand.plus.mapsource.EditMapSourceDialogFragment.OnMapSourceUpdateListener; @@ -74,6 +75,7 @@ import java.util.List; import java.util.Map; import java.util.Set; + public class LocalIndexesFragment extends OsmandExpandableListFragment implements DownloadEvents, OnMapSourceUpdateListener, RenameCallback { private LoadLocalIndexTask asyncLoader; @@ -351,10 +353,10 @@ public class LocalIndexesFragment extends OsmandExpandableListFragment implement getMyApplication().getResourceManager().closeFile(info.getFileName()); File tShm = new File(f.getParentFile(), f.getName() + "-shm"); File tWal = new File(f.getParentFile(), f.getName() + "-wal"); - if(tShm.exists()) { + if (tShm.exists()) { Algorithms.removeAllFiles(tShm); } - if(tWal.exists()) { + if (tWal.exists()) { Algorithms.removeAllFiles(tWal); } } @@ -370,8 +372,8 @@ public class LocalIndexesFragment extends OsmandExpandableListFragment implement getMyApplication().getResourceManager().closeFile(info.getFileName()); } } else if (operation == CLEAR_TILES_OPERATION) { - ITileSource src = (ITileSource) info.getAttachedObject(); - if(src != null) { + ITileSource src = (ITileSource) info.getAttachedObject(); + if (src != null) { src.deleteTiles(info.getPathToData()); } } @@ -419,10 +421,10 @@ public class LocalIndexesFragment extends OsmandExpandableListFragment implement @Override protected void onPostExecute(String result) { a.setProgressBarIndeterminateVisibility(false); - if(result != null && result.length() > 0) { + if (result != null && result.length() > 0) { Toast.makeText(a, result, Toast.LENGTH_LONG).show(); } - + if (operation == RESTORE_OPERATION || operation == BACKUP_OPERATION || operation == CLEAR_TILES_OPERATION) { a.reloadLocalIndexes(); } else { @@ -507,7 +509,7 @@ public class LocalIndexesFragment extends OsmandExpandableListFragment implement ItemClickListener listener = new ContextMenuAdapter.ItemClickListener() { @Override public boolean onContextMenuClick(ArrayAdapter adapter, - int itemId, int pos, boolean isChecked, int[] viewCoordinates) { + int itemId, int pos, boolean isChecked, int[] viewCoordinates) { localOptionsMenu(itemId); return true; } @@ -608,7 +610,7 @@ public class LocalIndexesFragment extends OsmandExpandableListFragment implement } private void openSelectionMode(final int actionResId, final int actionIconId, - final DialogInterface.OnClickListener listener) { + final DialogInterface.OnClickListener listener) { final int colorResId = getMyApplication().getSettings().isLightContent() ? R.color.active_buttons_and_links_text_light : R.color.active_buttons_and_links_text_dark; String value = getString(actionResId); if (value.endsWith("...")) { @@ -709,7 +711,7 @@ public class LocalIndexesFragment extends OsmandExpandableListFragment implement } public void openSelectionMode(int stringRes, int darkIcon, DialogInterface.OnClickListener listener, - EnumSet filter) { + EnumSet filter) { if (filter != null) { listAdapter.filterCategories(filter); } @@ -860,7 +862,7 @@ public class LocalIndexesFragment extends OsmandExpandableListFragment implement @Override public View getChildView(final int groupPosition, final int childPosition, - boolean isLastChild, View convertView, ViewGroup parent) { + boolean isLastChild, View convertView, ViewGroup parent) { LocalIndexInfoViewHolder viewHolder; if (convertView == null) { LayoutInflater inflater = LayoutInflater.from(ctx); @@ -878,8 +880,8 @@ public class LocalIndexesFragment extends OsmandExpandableListFragment implement private String getNameToDisplay(LocalIndexInfo child) { return child.getType() == LocalIndexType.VOICE_DATA ? FileNameTranslationHelper.getVoiceName(ctx, child.getFileName()) : FileNameTranslationHelper.getFileName(ctx, - ctx.getMyApplication().getResourceManager().getOsmandRegions(), - child.getFileName()); + ctx.getMyApplication().getResourceManager().getOsmandRegions(), + child.getFileName()); } @Override @@ -963,7 +965,7 @@ public class LocalIndexesFragment extends OsmandExpandableListFragment implement return ctx.getString(R.string.download_roads_only_item); } else if (child.isBackupedData() && child.getFileName().endsWith(IndexConstants.BINARY_WIKI_MAP_INDEX_EXT)) { return ctx.getString(R.string.download_wikipedia_maps); - } else if (child.isBackupedData() && child.getFileName().endsWith(IndexConstants.BINARY_SRTM_MAP_INDEX_EXT)) { + } else if (child.isBackupedData() && (SrtmDownloadItem.isSrtmFile(child.getFileName()))) { return ctx.getString(R.string.download_srtm_maps); } return ""; @@ -1029,6 +1031,10 @@ public class LocalIndexesFragment extends OsmandExpandableListFragment implement builder.append(AndroidUtils.formatSize(ctx, child.getSize() * 1024l)); } + if (SrtmDownloadItem.isSRTMItem(child)) { + builder.append(" ").append(SrtmDownloadItem.getAbbreviationInScopes(ctx, child)); + } + if (!Algorithms.isEmpty(child.getDescription())) { if (builder.length() > 0) { builder.append(" • "); @@ -1150,5 +1156,4 @@ public class LocalIndexesFragment extends OsmandExpandableListFragment implement private DownloadActivity getDownloadActivity() { return (DownloadActivity) getActivity(); } - } diff --git a/OsmAnd/src/net/osmand/plus/download/ui/SearchDialogFragment.java b/OsmAnd/src/net/osmand/plus/download/ui/SearchDialogFragment.java index ac9a87b32e..e6e3508ade 100644 --- a/OsmAnd/src/net/osmand/plus/download/ui/SearchDialogFragment.java +++ b/OsmAnd/src/net/osmand/plus/download/ui/SearchDialogFragment.java @@ -382,10 +382,10 @@ public class SearchDialogFragment extends DialogFragment implements DownloadEven if (obj instanceof IndexItem) { IndexItem item = (IndexItem) obj; viewHolder.setShowTypeInDesc(true); - viewHolder.bindIndexItem(item); + viewHolder.bindDownloadItem(item); } else { CityItem item = (CityItem) obj; - viewHolder.bindIndexItem(item); + viewHolder.bindDownloadItem(item); if (item.getIndexItem() == null) { new IndexItemResolverTask(viewHolder, item).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } @@ -461,7 +461,7 @@ public class SearchDialogFragment extends DialogFragment implements DownloadEven if (viewHolder != null) { if (indexItem != null) { cityItem.setIndexItem(indexItem); - viewHolder.bindIndexItem(indexItem, cityItem.getName()); + viewHolder.bindDownloadItem(indexItem, cityItem.getName()); } } } diff --git a/OsmAnd/src/net/osmand/plus/download/ui/UpdatesIndexFragment.java b/OsmAnd/src/net/osmand/plus/download/ui/UpdatesIndexFragment.java index ae0ab9b560..2cf1555428 100644 --- a/OsmAnd/src/net/osmand/plus/download/ui/UpdatesIndexFragment.java +++ b/OsmAnd/src/net/osmand/plus/download/ui/UpdatesIndexFragment.java @@ -410,7 +410,7 @@ public class UpdatesIndexFragment extends OsmAndListFragment implements Download holder.setShowRemoteDate(true); holder.setShowTypeInDesc(true); holder.setShowParentRegionName(true); - holder.bindIndexItem(getItem(position)); + holder.bindDownloadItem(getItem(position)); } return view; } diff --git a/OsmAnd/src/net/osmand/plus/firstusage/FirstUsageWizardFragment.java b/OsmAnd/src/net/osmand/plus/firstusage/FirstUsageWizardFragment.java index f8c3935b25..8bbc8c456e 100644 --- a/OsmAnd/src/net/osmand/plus/firstusage/FirstUsageWizardFragment.java +++ b/OsmAnd/src/net/osmand/plus/firstusage/FirstUsageWizardFragment.java @@ -5,7 +5,6 @@ import android.content.DialogInterface; import android.content.Intent; import android.os.AsyncTask; import android.os.Bundle; -import android.os.StatFs; import android.provider.Settings.Secure; import android.util.Log; import android.view.LayoutInflater; @@ -37,7 +36,6 @@ import net.osmand.plus.AppInitializer.AppInitializeListener; import net.osmand.plus.OsmAndLocationProvider; import net.osmand.plus.OsmAndLocationProvider.OsmAndLocationListener; import net.osmand.plus.OsmandApplication; -import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.R; import net.osmand.plus.Version; import net.osmand.plus.activities.MapActivity; @@ -51,12 +49,12 @@ import net.osmand.plus.download.IndexItem; import net.osmand.plus.download.ui.DataStoragePlaceDialogFragment; import net.osmand.plus.helpers.AndroidUiHelper; import net.osmand.plus.resources.ResourceManager; +import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.util.Algorithms; import net.osmand.util.MapUtils; import org.json.JSONObject; -import java.io.File; import java.io.IOException; import java.util.Iterator; import java.util.LinkedHashMap; @@ -331,7 +329,7 @@ public class FirstUsageWizardFragment extends BaseOsmAndFragment implements OsmA FragmentActivity activity = getActivity(); if (!OsmAndLocationProvider.isLocationPermissionAvailable(activity)) { ActivityCompat.requestPermissions(activity, - new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, + new String[] {Manifest.permission.ACCESS_FINE_LOCATION}, FIRST_USAGE_LOCATION_PERMISSION); } else { app.getLocationProvider().addLocationListener(this); @@ -387,13 +385,13 @@ public class FirstUsageWizardFragment extends BaseOsmAndFragment implements OsmA @Override public void onResume() { super.onResume(); - ((MapActivity)getActivity()).disableDrawer(); + ((MapActivity) getActivity()).disableDrawer(); } @Override public void onPause() { super.onPause(); - ((MapActivity)getActivity()).enableDrawer(); + ((MapActivity) getActivity()).enableDrawer(); } @Override @@ -697,7 +695,7 @@ public class FirstUsageWizardFragment extends BaseOsmAndFragment implements OsmA TextView freeSpaceValue = (TextView) storageView.findViewById(R.id.storage_free_space_value); String freeSpaceStr = getString(R.string.storage_free_space) + ": "; freeSpace.setText(freeSpaceStr); - freeSpaceValue.setText(getFreeSpace(settings.getExternalStorageDirectory())); + freeSpaceValue.setText(AndroidUtils.getFreeSpace(storageView.getContext(), settings.getExternalStorageDirectory())); AppCompatButton changeStorageButton = (AppCompatButton) storageView.findViewById(R.id.storage_change_button); if (wizardType == WizardType.MAP_DOWNLOAD) { @@ -709,7 +707,7 @@ public class FirstUsageWizardFragment extends BaseOsmAndFragment implements OsmA public void onClick(View v) { if (!DownloadActivity.hasPermissionToWriteExternalStorage(getContext())) { ActivityCompat.requestPermissions(getActivity(), - new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, + new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, FIRST_USAGE_REQUEST_WRITE_EXTERNAL_STORAGE_PERMISSION); } else { @@ -737,18 +735,6 @@ public class FirstUsageWizardFragment extends BaseOsmAndFragment implements OsmA } } - private String getFreeSpace(File dir) { - if (dir.canRead()) { - try { - StatFs fs = new StatFs(dir.getAbsolutePath()); - return AndroidUtils.formatSize(getActivity(), (long) fs.getAvailableBlocks() * fs.getBlockSize()); - } catch (IllegalArgumentException e) { - LOG.error(e); - } - } - return ""; - } - public static void showSearchLocationFragment(FragmentActivity activity, boolean searchByIp) { Fragment fragment = new FirstUsageWizardFragment(); Bundle args = new Bundle(); diff --git a/OsmAnd/src/net/osmand/plus/helpers/FileNameTranslationHelper.java b/OsmAnd/src/net/osmand/plus/helpers/FileNameTranslationHelper.java index e366d1e8bb..203088d44e 100644 --- a/OsmAnd/src/net/osmand/plus/helpers/FileNameTranslationHelper.java +++ b/OsmAnd/src/net/osmand/plus/helpers/FileNameTranslationHelper.java @@ -1,9 +1,11 @@ package net.osmand.plus.helpers; import android.content.Context; + import net.osmand.IndexConstants; import net.osmand.PlatformUtil; import net.osmand.map.OsmandRegions; +import net.osmand.map.WorldRegion; import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; import net.osmand.plus.download.DownloadResources; @@ -19,6 +21,7 @@ import java.lang.reflect.Field; public class FileNameTranslationHelper { private static final Log LOG = PlatformUtil.getLog(FileNameTranslationHelper.class); public static final String WIKI_NAME = "_wiki"; + public static final String WIKIVOYAGE_NAME = "_wikivoyage"; public static final String HILL_SHADE = "Hillshade"; public static final String SLOPE = "Slope"; public static final String SEA_DEPTH = "Depth_"; @@ -31,6 +34,8 @@ public class FileNameTranslationHelper { String basename = getBasename(fileName); if (basename.endsWith(WIKI_NAME)) { //wiki files return getWikiName(ctx, basename); + } else if (basename.endsWith(WIKIVOYAGE_NAME)) { + return getWikivoyageName(ctx, basename); } else if (fileName.endsWith("tts")) { //tts files return getVoiceName(ctx, fileName); } else if (fileName.endsWith(IndexConstants.FONT_INDEX_EXT)) { //otf files @@ -75,10 +80,10 @@ public class FileNameTranslationHelper { return ctx.getString(R.string.ltr_or_rtl_combine_via_space, locName, "(" + terrain + ")"); } - public static String getWikiName(Context ctx, String basename){ + public static String getWikiName(Context ctx, String basename) { String cutted = basename.substring(0, basename.indexOf("_wiki")); String wikiName = getStandardLangName(ctx, cutted); - if (wikiName == null){ + if (wikiName == null) { wikiName = cutted; } String wikiWord = ctx.getString(R.string.amenity_type_osmwiki); @@ -87,7 +92,18 @@ public class FileNameTranslationHelper { //removing word in "()" from recourse file return wikiName + " " + wikiWord.substring(0, index).trim(); } - return wikiName + " " + ctx.getString(R.string.amenity_type_osmwiki); + return wikiName + " " + ctx.getString(R.string.amenity_type_osmwiki); + } + + public static String getWikivoyageName(Context ctx, String basename) { + String formattedName = basename.substring(0, basename.indexOf(WIKIVOYAGE_NAME)).replaceAll("-", "").replaceAll("all", ""); + String wikiVoyageName = getSuggestedWikivoyageMaps(ctx, formattedName); + if (wikiVoyageName == null) { + wikiVoyageName = formattedName; + } + String wikiVoyageWord = ctx.getString(R.string.shared_string_wikivoyage); + + return ctx.getString(R.string.ltr_or_rtl_combine_via_space, wikiVoyageName, wikiVoyageWord); } public static String getVoiceName(Context ctx, String fileName) { @@ -196,8 +212,8 @@ public class FileNameTranslationHelper { return ctx.getString(R.string.lang_pl); } else if (filename.equalsIgnoreCase("Portuguese")) { return ctx.getString(R.string.lang_pt); - //} else if (filename.equalsIgnoreCase("Portuguese")) { - // return ctx.getString(R.string.lang_pt_br); + //} else if (filename.equalsIgnoreCase("Portuguese")) { + // return ctx.getString(R.string.lang_pt_br); } else if (filename.equalsIgnoreCase("Romanian")) { return ctx.getString(R.string.lang_ro); } else if (filename.equalsIgnoreCase("Russian")) { @@ -227,11 +243,11 @@ public class FileNameTranslationHelper { return ctx.getString(R.string.index_item_world_altitude_correction); } else if (basename.equals("world_basemap")) { return ctx.getString(R.string.index_item_world_basemap); - } else if (basename.equals("world_basemap_detailed")){ + } else if (basename.equals("world_basemap_detailed")) { return ctx.getString(R.string.index_item_world_basemap_detailed); } else if (basename.equals("world_bitcoin_payments")) { return ctx.getString(R.string.index_item_world_bitcoin_payments); - } else if (basename.equals(DownloadResources.WORLD_SEAMARKS_KEY) || + } else if (basename.equals(DownloadResources.WORLD_SEAMARKS_KEY) || basename.equals(DownloadResources.WORLD_SEAMARKS_OLD_KEY)) { return ctx.getString(R.string.index_item_world_seamarks); } else if (basename.equals("world_wikivoyage")) { @@ -245,4 +261,27 @@ public class FileNameTranslationHelper { } return null; } + + private static String getSuggestedWikivoyageMaps(Context ctx, String filename) { + if (WorldRegion.AFRICA_REGION_ID.equalsIgnoreCase(filename)) { + return ctx.getString(R.string.index_name_africa); + } else if (WorldRegion.AUSTRALIA_AND_OCEANIA_REGION_ID.replaceAll("-", "").equalsIgnoreCase(filename)) { + return ctx.getString(R.string.index_name_oceania); + } else if (WorldRegion.ASIA_REGION_ID.equalsIgnoreCase(filename)) { + return ctx.getString(R.string.index_name_asia); + } else if (WorldRegion.CENTRAL_AMERICA_REGION_ID.equalsIgnoreCase(filename)) { + return ctx.getString(R.string.index_name_central_america); + } else if (WorldRegion.EUROPE_REGION_ID.equalsIgnoreCase(filename)) { + return ctx.getString(R.string.index_name_europe); + } else if (WorldRegion.RUSSIA_REGION_ID.equalsIgnoreCase(filename)) { + return ctx.getString(R.string.index_name_russia); + } else if (WorldRegion.NORTH_AMERICA_REGION_ID.equalsIgnoreCase(filename)) { + return ctx.getString(R.string.index_name_north_america); + } else if (WorldRegion.SOUTH_AMERICA_REGION_ID.equalsIgnoreCase(filename)) { + return ctx.getString(R.string.index_name_south_america); + } else if (WorldRegion.ANTARCTICA_REGION_ID.equalsIgnoreCase(filename)) { + return ctx.getString(R.string.index_name_antarctica); + } + return null; + } } diff --git a/OsmAnd/src/net/osmand/plus/helpers/GpxUiHelper.java b/OsmAnd/src/net/osmand/plus/helpers/GpxUiHelper.java index 75d62d6f0a..849b8510ab 100644 --- a/OsmAnd/src/net/osmand/plus/helpers/GpxUiHelper.java +++ b/OsmAnd/src/net/osmand/plus/helpers/GpxUiHelper.java @@ -79,6 +79,7 @@ import net.osmand.plus.ContextMenuItem; import net.osmand.plus.GPXDatabase.GpxDataItem; import net.osmand.plus.GpxDbHelper; import net.osmand.plus.GpxDbHelper.GpxDataItemCallback; +import net.osmand.plus.GpxSelectionHelper; import net.osmand.plus.GpxSelectionHelper.GpxDisplayGroup; import net.osmand.plus.GpxSelectionHelper.GpxDisplayItem; import net.osmand.plus.GpxSelectionHelper.SelectedGpxFile; @@ -2233,17 +2234,18 @@ public class GpxUiHelper { return dataSet; } - public static GpxDisplayItem makeGpxDisplayItem(OsmandApplication app, GPXUtilities.GPXFile gpx) { - GpxDisplayItem gpxItem = null; - String groupName = app.getString(R.string.current_route); - GpxDisplayGroup group = app.getSelectedGpxHelper().buildGpxDisplayGroup(gpx, 0, groupName); + public static GpxDisplayItem makeGpxDisplayItem(OsmandApplication app, GPXFile gpxFile) { + GpxSelectionHelper helper = app.getSelectedGpxHelper(); + String groupName = helper.getGroupName(gpxFile); + GpxDisplayGroup group = helper.buildGpxDisplayGroup(gpxFile, 0, groupName); if (group != null && group.getModifiableList().size() > 0) { - gpxItem = group.getModifiableList().get(0); + GpxDisplayItem gpxItem = group.getModifiableList().get(0); if (gpxItem != null) { gpxItem.route = true; } + return gpxItem; } - return gpxItem; + return null; } public static void saveAndShareGpx(@NonNull final Context context, @NonNull final GPXFile gpxFile) { diff --git a/OsmAnd/src/net/osmand/plus/helpers/IntentHelper.java b/OsmAnd/src/net/osmand/plus/helpers/IntentHelper.java index 6fce7b78a6..5d540d5170 100644 --- a/OsmAnd/src/net/osmand/plus/helpers/IntentHelper.java +++ b/OsmAnd/src/net/osmand/plus/helpers/IntentHelper.java @@ -18,8 +18,8 @@ import net.osmand.plus.R; import net.osmand.plus.activities.MapActivity; import net.osmand.plus.activities.PluginsFragment; import net.osmand.plus.dashboard.DashboardOnMap.DashboardType; -import net.osmand.plus.itinerary.ItineraryGroup; import net.osmand.plus.mapmarkers.MapMarkersDialogFragment; +import net.osmand.plus.mapmarkers.MapMarkersGroup; import net.osmand.plus.mapsource.EditMapSourceDialogFragment; import net.osmand.plus.openplacereviews.OPRConstants; import net.osmand.plus.openplacereviews.OprAuthHelper.OprAuthorizationListener; @@ -219,7 +219,7 @@ public class IntentHelper { if (intent.hasExtra(MapMarkersDialogFragment.OPEN_MAP_MARKERS_GROUPS)) { Bundle openMapMarkersGroupsExtra = intent.getBundleExtra(MapMarkersDialogFragment.OPEN_MAP_MARKERS_GROUPS); if (openMapMarkersGroupsExtra != null) { - MapMarkersDialogFragment.showInstance(mapActivity, openMapMarkersGroupsExtra.getString(ItineraryGroup.MARKERS_SYNC_GROUP_ID)); + MapMarkersDialogFragment.showInstance(mapActivity, openMapMarkersGroupsExtra.getString(MapMarkersGroup.MARKERS_SYNC_GROUP_ID)); } mapActivity.setIntent(null); } diff --git a/OsmAnd/src/net/osmand/plus/importfiles/FavoritesImportTask.java b/OsmAnd/src/net/osmand/plus/importfiles/FavoritesImportTask.java index b62de4923c..703e920abc 100644 --- a/OsmAnd/src/net/osmand/plus/importfiles/FavoritesImportTask.java +++ b/OsmAnd/src/net/osmand/plus/importfiles/FavoritesImportTask.java @@ -8,6 +8,7 @@ import androidx.fragment.app.FragmentActivity; import net.osmand.GPXUtilities.GPXFile; import net.osmand.data.FavouritePoint; import net.osmand.plus.FavouritesDbHelper; +import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; import net.osmand.plus.base.BaseLoadAsyncTask; @@ -17,7 +18,7 @@ import static net.osmand.plus.importfiles.ImportHelper.asFavourites; import static net.osmand.plus.myplaces.FavoritesActivity.FAV_TAB; import static net.osmand.plus.myplaces.FavoritesActivity.TAB_ID; -class FavoritesImportTask extends BaseLoadAsyncTask { +public class FavoritesImportTask extends BaseLoadAsyncTask { private GPXFile gpxFile; private String fileName; @@ -33,6 +34,12 @@ class FavoritesImportTask extends BaseLoadAsyncTask { @Override protected GPXFile doInBackground(Void... nothing) { + mergeFavorites(app, gpxFile, fileName, forceImportFavourites); + return null; + } + + public static void mergeFavorites(@NonNull OsmandApplication app, @NonNull GPXFile gpxFile, + @NonNull String fileName, boolean forceImportFavourites) { List favourites = asFavourites(app, gpxFile.getPoints(), fileName, forceImportFavourites); FavouritesDbHelper favoritesHelper = app.getFavorites(); checkDuplicateNames(favourites); @@ -42,10 +49,9 @@ class FavoritesImportTask extends BaseLoadAsyncTask { } favoritesHelper.sortAll(); favoritesHelper.saveCurrentPointsIntoFile(); - return null; } - public void checkDuplicateNames(List favourites) { + public static void checkDuplicateNames(List favourites) { for (FavouritePoint fp : favourites) { int number = 1; String index; diff --git a/OsmAnd/src/net/osmand/plus/inapp/InAppPurchaseHelper.java b/OsmAnd/src/net/osmand/plus/inapp/InAppPurchaseHelper.java index 5136cd1f73..8a54df1b86 100644 --- a/OsmAnd/src/net/osmand/plus/inapp/InAppPurchaseHelper.java +++ b/OsmAnd/src/net/osmand/plus/inapp/InAppPurchaseHelper.java @@ -10,7 +10,7 @@ import android.util.Log; import net.osmand.AndroidNetworkUtils; import net.osmand.AndroidNetworkUtils.OnRequestResultListener; -import net.osmand.AndroidNetworkUtils.OnRequestsResultListener; +import net.osmand.AndroidNetworkUtils.OnSendRequestsListener; import net.osmand.AndroidNetworkUtils.RequestResponse; import net.osmand.PlatformUtil; import net.osmand.plus.OsmandApplication; @@ -21,6 +21,7 @@ import net.osmand.plus.inapp.InAppPurchases.InAppPurchase.PurchaseState; import net.osmand.plus.inapp.InAppPurchases.InAppSubscription; import net.osmand.plus.inapp.InAppPurchases.InAppSubscription.SubscriptionState; import net.osmand.plus.inapp.InAppPurchases.InAppSubscriptionList; +import net.osmand.plus.inapp.InAppPurchases.PurchaseInfo; import net.osmand.plus.liveupdates.CountrySelectionFragment; import net.osmand.plus.liveupdates.CountrySelectionFragment.CountryItem; import net.osmand.plus.settings.backend.OsmandSettings; @@ -52,7 +53,7 @@ public abstract class InAppPurchaseHelper { protected InAppPurchases purchases; protected long lastValidationCheckTime; protected boolean inventoryRequested; - protected Map subscriptionStateMap = new HashMap<>(); + protected Map subscriptionStateMap = new HashMap<>(); private static final long PURCHASE_VALIDATION_PERIOD_MSEC = 1000 * 60 * 60 * 24; // daily @@ -85,6 +86,11 @@ public abstract class InAppPurchaseHelper { void onFail(); } + static class SubscriptionStateHolder { + SubscriptionState state = SubscriptionState.UNDEFINED; + long expireTime = 0; + } + public enum InAppPurchaseTaskType { REQUEST_INVENTORY, PURCHASE_FULL_VERSION, @@ -112,30 +118,6 @@ public abstract class InAppPurchaseHelper { void onCommandDone(@NonNull InAppCommand command); } - public static class PurchaseInfo { - private String sku; - private String orderId; - private String purchaseToken; - - public PurchaseInfo(String sku, String orderId, String purchaseToken) { - this.sku = sku; - this.orderId = orderId; - this.purchaseToken = purchaseToken; - } - - public String getSku() { - return sku; - } - - public String getOrderId() { - return orderId; - } - - public String getPurchaseToken() { - return purchaseToken; - } - } - public String getToken() { return token; } @@ -193,6 +175,11 @@ public abstract class InAppPurchaseHelper { return purchases.getPurchasedMonthlyLiveUpdates(); } + @Nullable + public InAppSubscription getAnyPurchasedSubscription() { + return purchases.getAnyPurchasedSubscription(); + } + public InAppPurchaseHelper(OsmandApplication ctx) { this.ctx = ctx; isDeveloperVersion = Version.isDeveloperVersion(ctx); @@ -202,8 +189,7 @@ public abstract class InAppPurchaseHelper { public List getEverMadeSubscriptions() { List subscriptions = new ArrayList<>(); for (InAppSubscription subscription : getLiveUpdates().getVisibleSubscriptions()) { - SubscriptionState state = subscription.getState(); - if (state != SubscriptionState.UNDEFINED) { + if (subscription.isPurchased() || subscription.getState() != SubscriptionState.UNDEFINED) { subscriptions.add(subscription); } } @@ -448,15 +434,22 @@ public abstract class InAppPurchaseHelper { } if (subscriptionsStateJson != null) { inventoryRequested = true; - Map subscriptionStateMap = new HashMap<>(); + Map subscriptionStateMap = new HashMap<>(); try { JSONArray subArrJson = new JSONArray(subscriptionsStateJson); for (int i = 0; i < subArrJson.length(); i++) { JSONObject subObj = subArrJson.getJSONObject(i); String sku = subObj.getString("sku"); String state = subObj.getString("state"); + long expireTime = 0; + if (subObj.has("expire_time")) { + expireTime = subObj.getLong("expire_time"); + } if (!Algorithms.isEmpty(sku) && !Algorithms.isEmpty(state)) { - subscriptionStateMap.put(sku, SubscriptionState.getByStateStr(state)); + SubscriptionStateHolder stateHolder = new SubscriptionStateHolder(); + stateHolder.state = SubscriptionState.getByStateStr(state); + stateHolder.expireTime = expireTime; + subscriptionStateMap.put(sku, stateHolder); } } } catch (JSONException e) { @@ -500,12 +493,14 @@ public abstract class InAppPurchaseHelper { protected void onPurchaseDone(PurchaseInfo info) { logDebug("Purchase successful."); - InAppPurchase liveUpdatesPurchase = getLiveUpdates().getSubscriptionBySku(info.getSku()); + InAppSubscription liveUpdatesPurchase = getLiveUpdates().getSubscriptionBySku(info.getSku()); if (liveUpdatesPurchase != null) { // bought live updates logDebug("Live updates subscription purchased."); final String sku = liveUpdatesPurchase.getSku(); liveUpdatesPurchase.setPurchaseState(PurchaseState.PURCHASED); + liveUpdatesPurchase.setPurchaseInfo(ctx, info); + liveUpdatesPurchase.setState(ctx, SubscriptionState.UNDEFINED); sendTokens(Collections.singletonList(info), new OnRequestResultListener() { @Override public void onResult(String result) { @@ -525,6 +520,7 @@ public abstract class InAppPurchaseHelper { } else if (info.getSku().equals(getFullVersion().getSku())) { // bought full version getFullVersion().setPurchaseState(PurchaseState.PURCHASED); + getFullVersion().setPurchaseInfo(ctx, info); logDebug("Full version purchased."); showToast(ctx.getString(R.string.full_version_thanks)); ctx.getSettings().FULL_VERSION_PURCHASED.set(true); @@ -536,6 +532,7 @@ public abstract class InAppPurchaseHelper { } else if (info.getSku().equals(getDepthContours().getSku())) { // bought sea depth contours getDepthContours().setPurchaseState(PurchaseState.PURCHASED); + getDepthContours().setPurchaseInfo(ctx, info); logDebug("Sea depth contours purchased."); showToast(ctx.getString(R.string.sea_depth_thanks)); ctx.getSettings().DEPTH_CONTOURS_PURCHASED.set(true); @@ -548,6 +545,7 @@ public abstract class InAppPurchaseHelper { } else if (info.getSku().equals(getContourLines().getSku())) { // bought contour lines getContourLines().setPurchaseState(PurchaseState.PURCHASED); + getContourLines().setPurchaseInfo(ctx, info); logDebug("Contours lines purchased."); showToast(ctx.getString(R.string.contour_lines_thanks)); ctx.getSettings().CONTOUR_LINES_PURCHASED.set(true); @@ -610,9 +608,14 @@ public abstract class InAppPurchaseHelper { addUserInfo(parameters); requests.add(new AndroidNetworkUtils.Request(url, parameters, userOperation, true, true)); } - AndroidNetworkUtils.sendRequestsAsync(ctx, requests, new OnRequestsResultListener() { + AndroidNetworkUtils.sendRequestsAsync(ctx, requests, new OnSendRequestsListener() { + @Override - public void onResult(@NonNull List results) { + public void onRequestSent(@NonNull RequestResponse response) { + } + + @Override + public void onRequestsSent(@NonNull List results) { for (RequestResponse rr : results) { String sku = rr.getRequest().getParameters().get("sku"); PurchaseInfo info = getPurchaseInfo(sku); diff --git a/OsmAnd/src/net/osmand/plus/inapp/InAppPurchases.java b/OsmAnd/src/net/osmand/plus/inapp/InAppPurchases.java index 27d359630e..61dae8f0e8 100644 --- a/OsmAnd/src/net/osmand/plus/inapp/InAppPurchases.java +++ b/OsmAnd/src/net/osmand/plus/inapp/InAppPurchases.java @@ -7,33 +7,41 @@ import android.text.Spannable; import android.text.SpannableStringBuilder; import android.text.style.ForegroundColorSpan; +import androidx.annotation.ColorInt; +import androidx.annotation.DrawableRes; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.StringRes; + import net.osmand.AndroidUtils; import net.osmand.Period; import net.osmand.Period.PeriodUnit; import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; import net.osmand.plus.helpers.FontCache; +import net.osmand.plus.settings.backend.CommonPreference; +import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.widgets.style.CustomTypefaceSpan; import net.osmand.util.Algorithms; +import org.json.JSONException; +import org.json.JSONObject; + import java.text.NumberFormat; import java.text.ParseException; import java.util.ArrayList; import java.util.Arrays; +import java.util.Calendar; import java.util.Collections; import java.util.Comparator; import java.util.Currency; +import java.util.Date; +import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import androidx.annotation.ColorInt; -import androidx.annotation.DrawableRes; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.StringRes; - public abstract class InAppPurchases { protected InAppPurchase fullVersion; @@ -47,6 +55,10 @@ public abstract class InAppPurchases { protected InAppPurchases(OsmandApplication ctx) { } + private static OsmandSettings getSettings(@NonNull Context ctx) { + return ((OsmandApplication) ctx.getApplicationContext()).getSettings(); + } + public InAppPurchase getFullVersion() { return fullVersion; } @@ -83,6 +95,17 @@ public abstract class InAppPurchases { return null; } + @Nullable + public InAppSubscription getAnyPurchasedSubscription() { + List allSubscriptions = liveUpdates.getAllSubscriptions(); + for (InAppSubscription subscription : allSubscriptions) { + if (subscription.isAnyPurchased()) { + return subscription; + } + } + return null; + } + public InAppSubscriptionList getLiveUpdates() { return liveUpdates; } @@ -231,7 +254,7 @@ public abstract class InAppPurchases { private double priceValue; private String priceCurrencyCode; private PurchaseState purchaseState = PurchaseState.UNKNOWN; - private long purchaseTime; + private PurchaseInfo purchaseInfo; double monthlyPriceValue; boolean donationSupported = false; @@ -253,6 +276,37 @@ public abstract class InAppPurchases { return sku; } + @Nullable + public String getOrderId() { + return purchaseInfo != null ? purchaseInfo.getOrderId() : null; + } + + private CommonPreference getPurchaseInfoPref(@NonNull Context ctx) { + return getSettings(ctx).registerStringPreference(sku + "_purchase_info", "").makeGlobal(); + } + + public boolean storePurchaseInfo(@NonNull Context ctx) { + PurchaseInfo purchaseInfo = this.purchaseInfo; + if (purchaseInfo != null) { + getPurchaseInfoPref(ctx).set(purchaseInfo.toJson()); + return true; + } + return false; + } + + public boolean restorePurchaseInfo(@NonNull Context ctx) { + String json = getPurchaseInfoPref(ctx).get(); + if (!Algorithms.isEmpty(json)) { + try { + purchaseInfo = new PurchaseInfo(json); + } catch (JSONException e) { + // ignore + } + return true; + } + return false; + } + public String getPrice(Context ctx) { if (!Algorithms.isEmpty(price)) { return price; @@ -266,11 +320,16 @@ public abstract class InAppPurchases { } public long getPurchaseTime() { - return purchaseTime; + return purchaseInfo != null ? purchaseInfo.getPurchaseTime() : 0; } - public void setPurchaseTime(long purchaseTime) { - this.purchaseTime = purchaseTime; + public PurchaseInfo getPurchaseInfo() { + return purchaseInfo; + } + + void setPurchaseInfo(@NonNull Context ctx, PurchaseInfo purchaseInfo) { + this.purchaseInfo = purchaseInfo; + storePurchaseInfo(ctx); } public String getDefaultPrice(Context ctx) { @@ -571,35 +630,33 @@ public abstract class InAppPurchases { public static abstract class InAppSubscription extends InAppPurchase { - private Map upgrades = new ConcurrentHashMap<>(); - private String skuNoVersion; + private final Map upgrades = new ConcurrentHashMap<>(); + private final String skuNoVersion; private String subscriptionPeriodString; private Period subscriptionPeriod; private boolean upgrade = false; private SubscriptionState state = SubscriptionState.UNDEFINED; - private SubscriptionState prevState = SubscriptionState.UNDEFINED; + private SubscriptionState previousState = SubscriptionState.UNDEFINED; + private long expireTime = 0; private InAppSubscriptionIntroductoryInfo introductoryInfo; public enum SubscriptionState { - UNDEFINED("undefined", 0, 0), - ACTIVE("active", R.string.osm_live_active, R.drawable.bg_osmand_live_active), - CANCELLED("cancelled", R.string.osmand_live_cancelled, R.drawable.bg_osmand_live_cancelled), - IN_GRACE_PERIOD("in_grace_period", R.string.in_grace_period, R.drawable.bg_osmand_live_active), - ON_HOLD("on_hold", R.string.on_hold, R.drawable.bg_osmand_live_cancelled), - PAUSED("paused", R.string.shared_string_paused, R.drawable.bg_osmand_live_cancelled), - EXPIRED("expired", R.string.expired, R.drawable.bg_osmand_live_cancelled); + UNDEFINED("undefined", R.string.shared_string_undefined), + ACTIVE("active", R.string.osm_live_active), + CANCELLED("cancelled", R.string.osmand_live_cancelled), + IN_GRACE_PERIOD("in_grace_period", R.string.in_grace_period), + ON_HOLD("on_hold", R.string.on_hold), + PAUSED("paused", R.string.shared_string_paused), + EXPIRED("expired", R.string.expired); private final String stateStr; @StringRes private final int stringRes; - @DrawableRes - private final int backgroundRes; - SubscriptionState(@NonNull String stateStr, @StringRes int stringRes, @DrawableRes int backgroundRes) { + SubscriptionState(@NonNull String stateStr, @StringRes int stringRes) { this.stateStr = stateStr; this.stringRes = stringRes; - this.backgroundRes = backgroundRes; } public String getStateStr() { @@ -611,11 +668,6 @@ public abstract class InAppPurchases { return stringRes; } - @DrawableRes - public int getBackgroundRes() { - return backgroundRes; - } - @NonNull public static SubscriptionState getByStateStr(@NonNull String stateStr) { for (SubscriptionState state : SubscriptionState.values()) { @@ -678,21 +730,76 @@ public abstract class InAppPurchases { return state; } - public void setState(@NonNull SubscriptionState state) { + public void setState(@NonNull Context ctx, @NonNull SubscriptionState state) { this.state = state; + storeState(ctx, state); } @NonNull - public SubscriptionState getPrevState() { - return prevState; - } - - public void setPrevState(@NonNull SubscriptionState prevState) { - this.prevState = prevState; + public SubscriptionState getPreviousState() { + return previousState; } public boolean hasStateChanged() { - return state != prevState; + return state != previousState; + } + + private CommonPreference getStatePref(@NonNull Context ctx) { + return getSettings(ctx).registerStringPreference(getSku() + "_state", "").makeGlobal(); + } + + void storeState(@NonNull Context ctx, @NonNull SubscriptionState state) { + getStatePref(ctx).set(state.getStateStr()); + } + + boolean restoreState(@NonNull Context ctx) { + String stateStr = getStatePref(ctx).get(); + if (!Algorithms.isEmpty(stateStr)) { + SubscriptionState state = SubscriptionState.getByStateStr(stateStr); + this.previousState = state; + this.state = state; + return true; + } + return false; + } + + public long getCalculatedExpiredTime() { + long purchaseTime = getPurchaseTime(); + Period period = getSubscriptionPeriod(); + if (purchaseTime == 0 || period == null || period.getUnit() == null) { + return 0; + } + Date date = new Date(purchaseTime); + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + calendar.add(period.getUnit().getCalendarIdx(), period.getNumberOfUnits()); + return calendar.getTimeInMillis(); + } + + public long getExpireTime() { + return expireTime; + } + + public void setExpireTime(@NonNull Context ctx, long expireTime) { + this.expireTime = expireTime; + storeExpireTime(ctx, expireTime); + } + + private CommonPreference getExpireTimePref(@NonNull Context ctx) { + return getSettings(ctx).registerLongPreference(getSku() + "_expire_time", 0L).makeGlobal(); + } + + boolean restoreExpireTime(@NonNull Context ctx) { + Long expireTime = getExpireTimePref(ctx).get(); + if (expireTime != null) { + this.expireTime = expireTime; + return true; + } + return false; + } + + void storeExpireTime(@NonNull Context ctx, long expireTime) { + getExpireTimePref(ctx).set(expireTime); } public boolean isAnyPurchased() { @@ -999,5 +1106,95 @@ public abstract class InAppPurchases { return null; } } + + public static class PurchaseInfo { + private String sku; + private String orderId; + private String purchaseToken; + private long purchaseTime; + private int purchaseState; + private boolean acknowledged; + private boolean autoRenewing; + + PurchaseInfo(String sku, String orderId, String purchaseToken, long purchaseTime, + int purchaseState, boolean acknowledged, boolean autoRenewing) { + this.sku = sku; + this.orderId = orderId; + this.purchaseToken = purchaseToken; + this.purchaseTime = purchaseTime; + this.purchaseState = purchaseState; + this.acknowledged = acknowledged; + this.autoRenewing = autoRenewing; + } + + PurchaseInfo(@NonNull String json) throws JSONException { + parseJson(json); + } + + public String getSku() { + return sku; + } + + public String getOrderId() { + return orderId; + } + + public String getPurchaseToken() { + return purchaseToken; + } + + public long getPurchaseTime() { + return purchaseTime; + } + + public int getPurchaseState() { + return purchaseState; + } + + public boolean isAcknowledged() { + return acknowledged; + } + + public boolean isAutoRenewing() { + return autoRenewing; + } + + public String toJson() { + Map jsonMap = new HashMap<>(); + jsonMap.put("sku", sku); + jsonMap.put("orderId", orderId); + jsonMap.put("purchaseToken", purchaseToken); + jsonMap.put("purchaseTime", purchaseTime); + jsonMap.put("purchaseState", purchaseState); + jsonMap.put("acknowledged", acknowledged); + jsonMap.put("autoRenewing", autoRenewing); + return new JSONObject(jsonMap).toString(); + } + + public void parseJson(@NonNull String json) throws JSONException { + JSONObject jsonObj = new JSONObject(json); + if (jsonObj.has("sku")) { + this.sku = jsonObj.getString("sku"); + } + if (jsonObj.has("orderId")) { + this.orderId = jsonObj.getString("orderId"); + } + if (jsonObj.has("purchaseToken")) { + this.purchaseToken = jsonObj.getString("purchaseToken"); + } + if (jsonObj.has("purchaseTime")) { + this.purchaseTime = jsonObj.getLong("purchaseTime"); + } + if (jsonObj.has("purchaseState")) { + this.purchaseState = jsonObj.getInt("purchaseState"); + } + if (jsonObj.has("acknowledged")) { + this.acknowledged = jsonObj.getBoolean("acknowledged"); + } + if (jsonObj.has("autoRenewing")) { + this.autoRenewing = jsonObj.getBoolean("autoRenewing"); + } + } + } } diff --git a/OsmAnd/src/net/osmand/plus/itinerary/ItineraryHelper.java b/OsmAnd/src/net/osmand/plus/itinerary/ItineraryHelper.java deleted file mode 100644 index f89d05c1f9..0000000000 --- a/OsmAnd/src/net/osmand/plus/itinerary/ItineraryHelper.java +++ /dev/null @@ -1,436 +0,0 @@ -package net.osmand.plus.itinerary; - -import android.os.AsyncTask; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import net.osmand.GPXUtilities.GPXFile; -import net.osmand.GPXUtilities.WptPt; -import net.osmand.IndexConstants; -import net.osmand.PlatformUtil; -import net.osmand.data.FavouritePoint; -import net.osmand.data.LatLon; -import net.osmand.plus.FavouritesDbHelper.FavoriteGroup; -import net.osmand.plus.GPXDatabase; -import net.osmand.plus.GpxSelectionHelper; -import net.osmand.plus.GpxSelectionHelper.SelectedGpxFile; -import net.osmand.plus.OsmandApplication; -import net.osmand.plus.mapmarkers.MapMarker; -import net.osmand.plus.mapmarkers.MapMarkersDbHelper; -import net.osmand.plus.mapmarkers.MapMarkersHelper; -import net.osmand.plus.mapmarkers.MapMarkersHelper.OnGroupSyncedListener; -import net.osmand.plus.wikivoyage.data.TravelArticle; -import net.osmand.plus.wikivoyage.data.TravelHelper; -import net.osmand.util.Algorithms; - -import org.apache.commons.logging.Log; - -import java.io.File; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -import static net.osmand.plus.mapmarkers.MapMarkersHelper.BY_DATE_ADDED_DESC; - -public class ItineraryHelper { - - private static final Log LOG = PlatformUtil.getLog(ItineraryHelper.class); - - private OsmandApplication app; - - private MapMarkersHelper markersHelper; - private MapMarkersDbHelper markersDbHelper; - - private ExecutorService executorService = Executors.newSingleThreadExecutor(); - - private List itineraryGroups = new ArrayList<>(); - private Set syncListeners = new HashSet<>(); - - public ItineraryHelper(@NonNull OsmandApplication app) { - this.app = app; - markersHelper = app.getMapMarkersHelper(); - markersDbHelper = app.getMapMarkersDbHelper(); - loadGroups(); - } - - public List getItineraryGroups() { - return itineraryGroups; - } - - public void syncAllGroupsAsync() { - for (ItineraryGroup group : itineraryGroups) { - if (group.getId() != null && group.getName() != null) { - runSynchronization(group); - } - } - } - - public void updateGroupWptCategories(@NonNull ItineraryGroup group, Set wptCategories) { - String id = group.getId(); - if (id != null) { - group.setWptCategories(wptCategories); - if (wptCategories != null) { - markersDbHelper.updateGroupCategories(id, group.getWptCategoriesString()); - } - } - } - - public void enableGroup(@NonNull ItineraryGroup gr) { - // check if group doesn't exist internally - if (!itineraryGroups.contains(gr)) { - addGroupInternally(gr); - } - if (gr.isDisabled()) { - updateGroupDisabled(gr, false); - } - runSynchronization(gr); - } - - public void updateGroups() { - for (ItineraryGroup group : itineraryGroups) { - markersHelper.updateGroup(group); - } - } - - public void updateGroupDisabled(@NonNull ItineraryGroup group, boolean disabled) { - String id = group.getId(); - if (id != null) { - markersDbHelper.updateGroupDisabled(id, disabled); - group.setDisabled(disabled); - } - } - - public List getMapMarkersFromDefaultGroups(boolean history) { - List mapMarkers = new ArrayList<>(); - for (ItineraryGroup group : itineraryGroups) { - if (group.getType() == ItineraryGroup.ANY_TYPE) { - for (MapMarker marker : group.getMarkers()) { - if (history && marker.history || !history && !marker.history) { - mapMarkers.add(marker); - } - } - } - } - return mapMarkers; - } - - private void loadGroups() { - Map groupsMap = markersDbHelper.getAllGroupsMap(); - List allMarkers = new ArrayList<>(markersHelper.getMapMarkers()); - allMarkers.addAll(markersHelper.getMapMarkersHistory()); - - Iterator> iterator = groupsMap.entrySet().iterator(); - while (iterator.hasNext()) { - ItineraryGroup group = iterator.next().getValue(); - if (group.getType() == ItineraryGroup.GPX_TYPE && !new File(group.getId()).exists()) { - markersDbHelper.removeMarkersGroup(group.getId()); - iterator.remove(); - } - } - - ItineraryGroup noGroup = null; - - for (MapMarker marker : allMarkers) { - ItineraryGroup group = groupsMap.get(marker.groupKey); - if (group == null) { - if (noGroup == null) { - noGroup = new ItineraryGroup(); - noGroup.setCreationDate(Long.MAX_VALUE); - } - noGroup.getMarkers().add(marker); - } else { - if (marker.creationDate < group.getCreationDate()) { - group.setCreationDate(marker.creationDate); - } - group.getMarkers().add(marker); - } - } - - itineraryGroups = new ArrayList<>(groupsMap.values()); - if (noGroup != null) { - markersHelper.sortMarkers(noGroup.getMarkers(), false, BY_DATE_ADDED_DESC); - addToGroupsList(noGroup); - } - - sortGroups(); - - for (ItineraryGroup group : itineraryGroups) { - markersHelper.updateGroup(group); - } - } - - public void addGroupInternally(ItineraryGroup gr) { - markersDbHelper.addGroup(gr); - markersHelper.addHistoryMarkersToGroup(gr); - addToGroupsList(gr); - } - - public void updateGpxShowAsMarkers(File file) { - GPXDatabase.GpxDataItem dataItem = app.getGpxDbHelper().getItem(file); - if (dataItem != null) { - app.getGpxDbHelper().updateShowAsMarkers(dataItem, true); - dataItem.setShowAsMarkers(true); - } - } - - public void addToGroupsList(ItineraryGroup group) { - List copyList = new ArrayList<>(itineraryGroups); - copyList.add(group); - itineraryGroups = copyList; - } - - public void removeFromGroupsList(ItineraryGroup group) { - List copyList = new ArrayList<>(itineraryGroups); - copyList.remove(group); - itineraryGroups = copyList; - } - - public void addSyncListener(OnGroupSyncedListener listener) { - syncListeners.add(listener); - } - - public void removeSyncListener(OnGroupSyncedListener listener) { - syncListeners.remove(listener); - } - - public void runSynchronization(final @NonNull ItineraryGroup group) { - app.runInUIThread(new Runnable() { - @Override - public void run() { - new SyncGroupTask(group).executeOnExecutor(executorService); - } - }); - } - - public ItineraryGroup getMarkersGroup(GPXFile gpx) { - if (gpx == null || gpx.path == null) { - return null; - } - return getMapMarkerGroupById(getMarkerGroupId(new File(gpx.path)), ItineraryGroup.GPX_TYPE); - } - - public ItineraryGroup getMarkersGroup(FavoriteGroup favGroup) { - return getMapMarkerGroupById(getMarkerGroupId(favGroup), ItineraryGroup.FAVORITES_TYPE); - } - - public ItineraryGroup addOrEnableGpxGroup(@NonNull File file) { - updateGpxShowAsMarkers(file); - ItineraryGroup gr = getMapMarkerGroupById(getMarkerGroupId(file), ItineraryGroup.GPX_TYPE); - if (gr == null) { - gr = createGPXMarkerGroup(file); - addGroupInternally(gr); - } - enableGroup(gr); - return gr; - } - - public ItineraryGroup addOrEnableGroup(@NonNull GPXFile file) { - updateGpxShowAsMarkers(new File(file.path)); - ItineraryGroup gr = getMarkersGroup(file); - if (gr == null) { - gr = createGPXMarkerGroup(new File(file.path)); - addGroupInternally(gr); - } - enableGroup(gr); - return gr; - } - - public ItineraryGroup addOrEnableGroup(@NonNull FavoriteGroup group) { - ItineraryGroup gr = getMarkersGroup(group); - if (gr == null) { - gr = createFavMarkerGroup(group); - addGroupInternally(gr); - } - enableGroup(gr); - return gr; - } - - private ItineraryGroup createGPXMarkerGroup(File fl) { - return new ItineraryGroup(getMarkerGroupId(fl), - Algorithms.getFileNameWithoutExtension(fl.getName()), - ItineraryGroup.GPX_TYPE); - } - - private ItineraryGroup createFavMarkerGroup(FavoriteGroup favGroup) { - return new ItineraryGroup(favGroup.getName(), favGroup.getName(), ItineraryGroup.FAVORITES_TYPE); - } - - private String getMarkerGroupId(File gpx) { - return gpx.getAbsolutePath(); - } - - private String getMarkerGroupId(FavoriteGroup group) { - return group.getName(); - } - - - public void removeMarkerFromGroup(MapMarker marker) { - if (marker != null) { - ItineraryGroup itineraryGroup = getMapMarkerGroupById(marker.groupKey, marker.getType()); - if (itineraryGroup != null) { - itineraryGroup.getMarkers().remove(marker); - markersHelper.updateGroup(itineraryGroup); - } - } - } - - public void sortGroups() { - if (itineraryGroups.size() > 0) { - Collections.sort(itineraryGroups, new Comparator() { - @Override - public int compare(ItineraryGroup group1, ItineraryGroup group2) { - long t1 = group1.getCreationDate(); - long t2 = group2.getCreationDate(); - return (t1 > t2) ? -1 : ((t1 == t2) ? 0 : 1); - } - }); - } - } - - @Nullable - public ItineraryGroup getMapMarkerGroupById(String id, int type) { - for (ItineraryGroup group : itineraryGroups) { - if ((id == null && group.getId() == null) - || (group.getId() != null && group.getId().equals(id))) { - if (type == ItineraryGroup.ANY_TYPE || type == group.getType()) { - return group; - } - } - } - return null; - } - - @NonNull - public List getGroupsForDisplayedGpx() { - List res = new ArrayList<>(); - List selectedGpxFiles = app.getSelectedGpxHelper().getSelectedGPXFiles(); - for (SelectedGpxFile selected : selectedGpxFiles) { - ItineraryGroup search = getMarkersGroup(selected.getGpxFile()); - if (search == null && selected.getGpxFile() != null && selected.getGpxFile().path != null) { - ItineraryGroup group = createGPXMarkerGroup(new File(selected.getGpxFile().path)); - group.setDisabled(true); - markersHelper.createHeadersInGroup(group); - res.add(group); - } - } - return res; - } - - @NonNull - public List getGroupsForSavedArticlesTravelBook() { - List res = new ArrayList<>(); - TravelHelper travelHelper = app.getTravelHelper(); - if (travelHelper.isAnyTravelBookPresent()) { - List savedArticles = travelHelper.getBookmarksHelper().getSavedArticles(); - for (TravelArticle art : savedArticles) { - String gpxName = travelHelper.getGPXName(art); - File path = app.getAppPath(IndexConstants.GPX_TRAVEL_DIR + gpxName); - ItineraryGroup search = getMapMarkerGroupById(getMarkerGroupId(path), ItineraryGroup.GPX_TYPE); - if (search == null) { - ItineraryGroup group = createGPXMarkerGroup(path); - group.setDisabled(true); - markersHelper.createHeadersInGroup(group); - res.add(group); - } - } - } - return res; - } - - private class SyncGroupTask extends AsyncTask { - - private ItineraryGroup group; - - SyncGroupTask(ItineraryGroup group) { - this.group = group; - } - - @Override - protected void onPreExecute() { - if (!syncListeners.isEmpty()) { - app.runInUIThread(new Runnable() { - @Override - public void run() { - for (OnGroupSyncedListener listener : syncListeners) { - listener.onSyncStarted(); - } - } - }); - } - } - - @Override - protected Void doInBackground(Void... voids) { - runGroupSynchronization(); - return null; - } - - // TODO extract method from Asynctask to Helper directly - private void runGroupSynchronization() { - List groupMarkers = new ArrayList<>(group.getMarkers()); - if (group.getType() == ItineraryGroup.FAVORITES_TYPE) { - FavoriteGroup favGroup = app.getFavorites().getGroup(group.getName()); - if (favGroup == null) { - return; - } - group.setVisible(favGroup.isVisible()); - if (!group.isVisible() || group.isDisabled()) { - markersHelper.removeGroupActiveMarkers(group, true); - return; - } - List points = new ArrayList<>(favGroup.getPoints()); - for (FavouritePoint fp : points) { - markersHelper.addNewMarkerIfNeeded(group, groupMarkers, new LatLon(fp.getLatitude(), fp.getLongitude()), fp.getName(), fp, null); - } - } else if (group.getType() == ItineraryGroup.GPX_TYPE) { - GpxSelectionHelper gpxHelper = app.getSelectedGpxHelper(); - File file = new File(group.getId()); - if (!file.exists()) { - return; - } - - String gpxPath = group.getId(); - SelectedGpxFile selectedGpxFile = gpxHelper.getSelectedFileByPath(gpxPath); - GPXFile gpx = selectedGpxFile == null ? null : selectedGpxFile.getGpxFile(); - group.setVisible(gpx != null || group.isVisibleUntilRestart()); - if (gpx == null || group.isDisabled()) { - markersHelper.removeGroupActiveMarkers(group, true); - return; - } - - boolean addAll = group.getWptCategories() == null || group.getWptCategories().isEmpty(); - List gpxPoints = new ArrayList<>(gpx.getPoints()); - for (WptPt pt : gpxPoints) { - if (addAll || group.getWptCategories().contains(pt.category) - || (pt.category == null && group.getWptCategories().contains(""))) { - markersHelper.addNewMarkerIfNeeded(group, groupMarkers, new LatLon(pt.lat, pt.lon), pt.name, null, pt); - } - } - } - markersHelper.removeOldMarkersIfPresent(groupMarkers); - } - - @Override - protected void onPostExecute(Void aVoid) { - if (!syncListeners.isEmpty()) { - app.runInUIThread(new Runnable() { - @Override - public void run() { - for (OnGroupSyncedListener listener : syncListeners) { - listener.onSyncDone(); - } - } - }); - } - } - } -} \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/liveupdates/LiveUpdatesFragment.java b/OsmAnd/src/net/osmand/plus/liveupdates/LiveUpdatesFragment.java index 9f91391af3..7bebeda1f2 100644 --- a/OsmAnd/src/net/osmand/plus/liveupdates/LiveUpdatesFragment.java +++ b/OsmAnd/src/net/osmand/plus/liveupdates/LiveUpdatesFragment.java @@ -275,7 +275,8 @@ public class LiveUpdatesFragment extends BaseOsmAndDialogFragment implements OnL TextViewEx toolbarTitle = (TextViewEx) toolbar.findViewById(R.id.toolbar_title); toolbarTitle.setText(R.string.osm_live); - View closeButton = toolbar.findViewById(R.id.close_button); + ImageView closeButton = (ImageView) toolbar.findViewById(R.id.close_button); + UiUtilities.rotateImageByLayoutDirection(closeButton); closeButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -440,6 +441,8 @@ public class LiveUpdatesFragment extends BaseOsmAndDialogFragment implements OnL boolean isLastChild, View convertView, ViewGroup parent) { LayoutInflater inflater = UiUtilities.getInflater(app, nightMode); convertView = inflater.inflate(R.layout.list_item_triple_row_icon_and_menu, parent, false); + ImageView secondaryIcon = (ImageView) convertView.findViewById(R.id.secondary_icon); + UiUtilities.rotateImageByLayoutDirection(secondaryIcon); LiveMapsViewHolder viewHolder = new LiveMapsViewHolder(convertView); convertView.setTag(viewHolder); viewHolder.bindLocalIndexInfo(getChild(groupPosition, childPosition).getFileName()); diff --git a/OsmAnd/src/net/osmand/plus/liveupdates/LiveUpdatesSettingsBottomSheet.java b/OsmAnd/src/net/osmand/plus/liveupdates/LiveUpdatesSettingsBottomSheet.java index a40d0e956c..1fb4fab1be 100644 --- a/OsmAnd/src/net/osmand/plus/liveupdates/LiveUpdatesSettingsBottomSheet.java +++ b/OsmAnd/src/net/osmand/plus/liveupdates/LiveUpdatesSettingsBottomSheet.java @@ -42,9 +42,10 @@ import net.osmand.plus.liveupdates.LiveUpdatesHelper.UpdateFrequency; import net.osmand.plus.resources.IncrementalChangesManager; import net.osmand.plus.settings.backend.CommonPreference; import net.osmand.plus.settings.backend.OsmandSettings; -import net.osmand.plus.widgets.MultiStateToggleButton; -import net.osmand.plus.widgets.MultiStateToggleButton.OnRadioItemClickListener; -import net.osmand.plus.widgets.MultiStateToggleButton.RadioItem; +import net.osmand.plus.widgets.multistatetoggle.RadioItem; +import net.osmand.plus.widgets.multistatetoggle.RadioItem.OnRadioItemClickListener; +import net.osmand.plus.widgets.multistatetoggle.TextToggleButton; +import net.osmand.plus.widgets.multistatetoggle.TextToggleButton.TextRadioItem; import net.osmand.plus.widgets.TextViewEx; import net.osmand.plus.widgets.style.CustomTypefaceSpan; import net.osmand.util.Algorithms; @@ -83,8 +84,8 @@ public class LiveUpdatesSettingsBottomSheet extends MenuBottomSheetDialogFragmen private BaseBottomSheetItem itemFrequencyHelpMessage; private BaseBottomSheetItem itemClear; private BaseBottomSheetItem itemViaWiFi; - private MultiStateToggleButton frequencyToggleButton; - private MultiStateToggleButton timeOfDayToggleButton; + private TextToggleButton frequencyToggleButton; + private TextToggleButton timeOfDayToggleButton; private String fileName; private OnLiveUpdatesForLocalChange onLiveUpdatesForLocalChange; @@ -195,9 +196,9 @@ public class LiveUpdatesSettingsBottomSheet extends MenuBottomSheetDialogFragmen String morning = getString(R.string.morning); String night = getString(R.string.night); - RadioItem morningButton = new RadioItem(morning); - RadioItem nightButton = new RadioItem(night); - timeOfDayToggleButton = new MultiStateToggleButton(app, itemTimeOfDayButtons, nightMode); + TextRadioItem morningButton = new TextRadioItem(morning); + TextRadioItem nightButton = new TextRadioItem(night); + timeOfDayToggleButton = new TextToggleButton(app, itemTimeOfDayButtons, nightMode); timeOfDayToggleButton.setItems(morningButton, nightButton); setSelectedRadioItem(timeOfDayToggleButton, timeOfDayPreference.get(), morningButton, nightButton); timeOfDayToggleButton.updateView(localUpdatePreference.get()); @@ -231,10 +232,10 @@ public class LiveUpdatesSettingsBottomSheet extends MenuBottomSheetDialogFragmen String hourly = getString(R.string.hourly); String daily = getString(R.string.daily); String weekly = getString(R.string.weekly); - RadioItem hourlyButton = new RadioItem(hourly); - RadioItem dailyButton = new RadioItem(daily); - RadioItem weeklyButton = new RadioItem(weekly); - frequencyToggleButton = new MultiStateToggleButton(app, itemFrequencyButtons, nightMode); + TextRadioItem hourlyButton = new TextRadioItem(hourly); + TextRadioItem dailyButton = new TextRadioItem(daily); + TextRadioItem weeklyButton = new TextRadioItem(weekly); + frequencyToggleButton = new TextToggleButton(app, itemFrequencyButtons, nightMode); frequencyToggleButton.setItems(hourlyButton, dailyButton, weeklyButton); setSelectedRadioItem(frequencyToggleButton, frequencyPreference.get(), hourlyButton, dailyButton, weeklyButton); frequencyToggleButton.updateView(localUpdatePreference.get()); @@ -467,7 +468,7 @@ public class LiveUpdatesSettingsBottomSheet extends MenuBottomSheetDialogFragmen return dividerItem; } - private void setSelectedRadioItem(MultiStateToggleButton toggleButton, int position, RadioItem... buttons) { + private void setSelectedRadioItem(TextToggleButton toggleButton, int position, TextRadioItem... buttons) { toggleButton.setSelectedItem(buttons[position]); } diff --git a/OsmAnd/src/net/osmand/plus/mapcontextmenu/controllers/SelectedGpxMenuController.java b/OsmAnd/src/net/osmand/plus/mapcontextmenu/controllers/SelectedGpxMenuController.java index 76b8ff1c7b..ef38b823ed 100644 --- a/OsmAnd/src/net/osmand/plus/mapcontextmenu/controllers/SelectedGpxMenuController.java +++ b/OsmAnd/src/net/osmand/plus/mapcontextmenu/controllers/SelectedGpxMenuController.java @@ -7,12 +7,13 @@ import android.os.AsyncTask; import androidx.annotation.NonNull; import net.osmand.AndroidUtils; -import net.osmand.GPXUtilities; import net.osmand.GPXUtilities.GPXFile; import net.osmand.GPXUtilities.WptPt; import net.osmand.data.LatLon; import net.osmand.data.PointDescription; import net.osmand.plus.GpxSelectionHelper; +import net.osmand.plus.GpxSelectionHelper.GpxDisplayGroup; +import net.osmand.plus.GpxSelectionHelper.GpxDisplayItem; import net.osmand.plus.GpxSelectionHelper.SelectedGpxFile; import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; @@ -24,7 +25,6 @@ import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.track.TrackMenuFragment; import net.osmand.util.Algorithms; -import java.io.File; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; @@ -93,27 +93,14 @@ public class SelectedGpxMenuController extends MenuController { @Override protected GpxSelectionHelper.GpxDisplayItem doInBackground(Void... voids) { - GpxSelectionHelper.GpxDisplayGroup gpxDisplayGroup = null; - GPXUtilities.GPXFile gpxFile = null; - GPXUtilities.Track generalTrack = null; - if (selectedGpxFile.getGpxFile().path != null) { - gpxFile = GPXUtilities.loadGPXFile(new File(selectedGpxFile.getGpxFile().path)); - } - if (gpxFile != null) { - generalTrack = gpxFile.getGeneralTrack(); - } - if (generalTrack != null) { - gpxFile.addGeneralTrack(); - gpxDisplayGroup = app.getSelectedGpxHelper().buildGeneralGpxDisplayGroup(gpxFile, generalTrack); - } else if (gpxFile != null && gpxFile.tracks.size() > 0) { - gpxDisplayGroup = app.getSelectedGpxHelper().buildGeneralGpxDisplayGroup(gpxFile, gpxFile.tracks.get(0)); - } - List items = null; - if (gpxDisplayGroup != null) { - items = gpxDisplayGroup.getModifiableList(); - } - if (items != null && items.size() > 0) { - return items.get(0); + GPXFile gpxFile = selectedGpxFile.getGpxFile(); + if (gpxFile.tracks.size() > 0) { + GpxDisplayGroup gpxDisplayGroup = app.getSelectedGpxHelper().buildGeneralGpxDisplayGroup(gpxFile, gpxFile.tracks.get(0)); + + List items = gpxDisplayGroup.getModifiableList(); + if (!Algorithms.isEmpty(items)) { + return items.get(0); + } } return null; } diff --git a/OsmAnd/src/net/osmand/plus/mapcontextmenu/controllers/TransportStopController.java b/OsmAnd/src/net/osmand/plus/mapcontextmenu/controllers/TransportStopController.java index 2337031400..5a7ac0d665 100644 --- a/OsmAnd/src/net/osmand/plus/mapcontextmenu/controllers/TransportStopController.java +++ b/OsmAnd/src/net/osmand/plus/mapcontextmenu/controllers/TransportStopController.java @@ -189,7 +189,24 @@ public class TransportStopController extends MenuController { } } - private static void sortTransportStops(@NonNull LatLon latLon, List transportStops) { + private static void sortTransportStopsExits(@NonNull LatLon latLon, @NonNull List transportStops) { + for (TransportStop transportStop : transportStops) { + for (TransportStopExit exit : transportStop.getExits()) { + int distance = (int) MapUtils.getDistance(latLon, exit.getLocation()); + if (transportStop.distance > distance) { + transportStop.distance = distance; + } + } + } + Collections.sort(transportStops, new Comparator() { + @Override + public int compare(TransportStop s1, TransportStop s2) { + return Algorithms.compare(s1.distance, s2.distance); + } + }); + } + + private static void sortTransportStops(@NonNull LatLon latLon, @NonNull List transportStops) { for (TransportStop transportStop : transportStops) { transportStop.distance = (int) MapUtils.getDistance(latLon, transportStop.getLocation()); } @@ -227,6 +244,8 @@ public class TransportStopController extends MenuController { if (isSubwayEntrance) { stopAggregated = processTransportStopsForAmenity(transportStops, amenity); + sortTransportStopsExits(loc, stopAggregated.getLocalTransportStops()); + sortTransportStopsExits(loc, stopAggregated.getNearbyTransportStops()); } else { stopAggregated = new TransportStopAggregated(); stopAggregated.setAmenity(amenity); diff --git a/OsmAnd/src/net/osmand/plus/mapcontextmenu/editors/WptPtEditorFragment.java b/OsmAnd/src/net/osmand/plus/mapcontextmenu/editors/WptPtEditorFragment.java index 713b13a177..2d0654301b 100644 --- a/OsmAnd/src/net/osmand/plus/mapcontextmenu/editors/WptPtEditorFragment.java +++ b/OsmAnd/src/net/osmand/plus/mapcontextmenu/editors/WptPtEditorFragment.java @@ -17,6 +17,8 @@ import net.osmand.GPXUtilities.WptPt; import net.osmand.data.LatLon; import net.osmand.data.WptLocationPoint; import net.osmand.plus.GpxSelectionHelper; +import net.osmand.plus.mapmarkers.MapMarkersHelper; +import net.osmand.plus.mapmarkers.MapMarkersGroup; import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; import net.osmand.plus.activities.MapActivity; @@ -24,7 +26,6 @@ import net.osmand.plus.activities.SavingTrackHelper; import net.osmand.plus.base.PointImageDrawable; import net.osmand.plus.mapcontextmenu.MapContextMenu; import net.osmand.plus.mapcontextmenu.editors.WptPtEditor.OnDismissListener; -import net.osmand.plus.itinerary.ItineraryGroup; import net.osmand.plus.track.SaveGpxAsyncTask; import net.osmand.plus.track.SaveGpxAsyncTask.SaveGpxListener; import net.osmand.util.Algorithms; @@ -219,9 +220,10 @@ public class WptPtEditorFragment extends PointEditorFragment { private void syncGpx(GPXFile gpxFile) { OsmandApplication app = getMyApplication(); if (app != null) { - ItineraryGroup group = app.getItineraryHelper().getMarkersGroup(gpxFile); + MapMarkersHelper helper = app.getMapMarkersHelper(); + MapMarkersGroup group = helper.getMarkersGroup(gpxFile); if (group != null) { - app.getItineraryHelper().runSynchronization(group); + helper.runSynchronization(group); } } } diff --git a/OsmAnd/src/net/osmand/plus/mapcontextmenu/editors/WptPtEditorFragmentNew.java b/OsmAnd/src/net/osmand/plus/mapcontextmenu/editors/WptPtEditorFragmentNew.java index 827151a691..7ecd61565f 100644 --- a/OsmAnd/src/net/osmand/plus/mapcontextmenu/editors/WptPtEditorFragmentNew.java +++ b/OsmAnd/src/net/osmand/plus/mapcontextmenu/editors/WptPtEditorFragmentNew.java @@ -20,6 +20,8 @@ import net.osmand.data.FavouritePoint.BackgroundType; import net.osmand.data.LatLon; import net.osmand.data.WptLocationPoint; import net.osmand.plus.GpxSelectionHelper; +import net.osmand.plus.mapmarkers.MapMarkersHelper; +import net.osmand.plus.mapmarkers.MapMarkersGroup; import net.osmand.plus.GpxSelectionHelper.SelectedGpxFile; import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; @@ -27,7 +29,6 @@ import net.osmand.plus.UiUtilities; import net.osmand.plus.activities.MapActivity; import net.osmand.plus.activities.SavingTrackHelper; import net.osmand.plus.base.PointImageDrawable; -import net.osmand.plus.itinerary.ItineraryGroup; import net.osmand.plus.mapcontextmenu.MapContextMenu; import net.osmand.plus.mapcontextmenu.editors.WptPtEditor.OnDismissListener; import net.osmand.plus.track.SaveGpxAsyncTask; @@ -237,9 +238,10 @@ public class WptPtEditorFragmentNew extends PointEditorFragmentNew { private void syncGpx(GPXFile gpxFile) { OsmandApplication app = getMyApplication(); if (app != null) { - ItineraryGroup group = app.getItineraryHelper().getMarkersGroup(gpxFile); + MapMarkersHelper helper = app.getMapMarkersHelper(); + MapMarkersGroup group = helper.getMarkersGroup(gpxFile); if (group != null) { - app.getItineraryHelper().runSynchronization(group); + helper.runSynchronization(group); } } } diff --git a/OsmAnd/src/net/osmand/plus/mapmarkers/AddFavouritesGroupBottomSheetDialogFragment.java b/OsmAnd/src/net/osmand/plus/mapmarkers/AddFavouritesGroupBottomSheetDialogFragment.java index 52f85bf0e7..0e6b31f4f4 100644 --- a/OsmAnd/src/net/osmand/plus/mapmarkers/AddFavouritesGroupBottomSheetDialogFragment.java +++ b/OsmAnd/src/net/osmand/plus/mapmarkers/AddFavouritesGroupBottomSheetDialogFragment.java @@ -56,7 +56,7 @@ public class AddFavouritesGroupBottomSheetDialogFragment extends AddGroupBottomS if (!group.isVisible()) { favouritesDbHelper.editFavouriteGroup(group, group.getName(), group.getColor(), true); } - getMyApplication().getItineraryHelper().addOrEnableGroup(group); + getMyApplication().getMapMarkersHelper().addOrEnableGroup(group); dismiss(); } } diff --git a/OsmAnd/src/net/osmand/plus/mapmarkers/AddTracksGroupBottomSheetDialogFragment.java b/OsmAnd/src/net/osmand/plus/mapmarkers/AddTracksGroupBottomSheetDialogFragment.java index 32dfd58172..b01548be00 100644 --- a/OsmAnd/src/net/osmand/plus/mapmarkers/AddTracksGroupBottomSheetDialogFragment.java +++ b/OsmAnd/src/net/osmand/plus/mapmarkers/AddTracksGroupBottomSheetDialogFragment.java @@ -108,7 +108,7 @@ public class AddTracksGroupBottomSheetDialogFragment extends AddGroupBottomSheet GPXFile res = GPXUtilities.loadGPXFile(gpx); selectionHelper.selectGpxFile(res, true, false, false, false, false); } - app.getItineraryHelper().addOrEnableGpxGroup(gpx); + app.getMapMarkersHelper().addOrEnableGpxGroup(gpx); } } dismiss(); diff --git a/OsmAnd/src/net/osmand/plus/mapmarkers/CategoriesSubHeader.java b/OsmAnd/src/net/osmand/plus/mapmarkers/CategoriesSubHeader.java index 52ea71617b..47353e409f 100644 --- a/OsmAnd/src/net/osmand/plus/mapmarkers/CategoriesSubHeader.java +++ b/OsmAnd/src/net/osmand/plus/mapmarkers/CategoriesSubHeader.java @@ -2,25 +2,3 @@ package net.osmand.plus.mapmarkers; import androidx.annotation.DrawableRes; -import net.osmand.plus.itinerary.ItineraryGroup; - -public class CategoriesSubHeader { - - @DrawableRes - private int iconRes; - private ItineraryGroup group; - - public CategoriesSubHeader(int iconRes, ItineraryGroup group) { - this.iconRes = iconRes; - this.group = group; - } - - @DrawableRes - public int getIconRes() { - return iconRes; - } - - public ItineraryGroup getGroup() { - return group; - } -} diff --git a/OsmAnd/src/net/osmand/plus/mapmarkers/CoordinateInputDialogFragment.java b/OsmAnd/src/net/osmand/plus/mapmarkers/CoordinateInputDialogFragment.java index 138109f126..96d6f9a8f4 100644 --- a/OsmAnd/src/net/osmand/plus/mapmarkers/CoordinateInputDialogFragment.java +++ b/OsmAnd/src/net/osmand/plus/mapmarkers/CoordinateInputDialogFragment.java @@ -71,8 +71,6 @@ import net.osmand.plus.Version; import net.osmand.plus.activities.SavingTrackHelper; import net.osmand.plus.activities.TrackActivity; import net.osmand.plus.helpers.AndroidUiHelper; -import net.osmand.plus.itinerary.ItineraryGroup; -import net.osmand.plus.itinerary.ItineraryHelper; import net.osmand.plus.mapmarkers.CoordinateInputBottomSheetDialogFragment.CoordinateInputFormatChangeListener; import net.osmand.plus.mapmarkers.CoordinateInputFormats.DDM; import net.osmand.plus.mapmarkers.CoordinateInputFormats.DMS; @@ -170,8 +168,8 @@ public class CoordinateInputDialogFragment extends DialogFragment implements Osm } private void syncGpx(GPXFile gpxFile) { - ItineraryHelper helper = getMyApplication().getItineraryHelper(); - ItineraryGroup group = helper.getMarkersGroup(gpxFile); + MapMarkersHelper helper = getMyApplication().getMapMarkersHelper(); + MapMarkersGroup group = helper.getMarkersGroup(gpxFile); if (group != null) { helper.runSynchronization(group); } @@ -1091,7 +1089,7 @@ public class CoordinateInputDialogFragment extends DialogFragment implements Osm public void saveGpx(final String fileName) { new SaveGpxAsyncTask(app, getGpx(),fileName, false).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); hasUnsavedChanges = false; - app.getItineraryHelper().addOrEnableGroup(getGpx()); + app.getMapMarkersHelper().addOrEnableGroup(getGpx()); if (listener != null) { listener.onPointsSaved(); } diff --git a/OsmAnd/src/net/osmand/plus/mapmarkers/GroupHeader.java b/OsmAnd/src/net/osmand/plus/mapmarkers/GroupHeader.java index c760b08dee..c5c97a1c2e 100644 --- a/OsmAnd/src/net/osmand/plus/mapmarkers/GroupHeader.java +++ b/OsmAnd/src/net/osmand/plus/mapmarkers/GroupHeader.java @@ -2,15 +2,13 @@ package net.osmand.plus.mapmarkers; import androidx.annotation.DrawableRes; -import net.osmand.plus.itinerary.ItineraryGroup; - public class GroupHeader { @DrawableRes private int iconRes; - private ItineraryGroup group; + private MapMarkersGroup group; - public GroupHeader(int iconRes, ItineraryGroup group) { + public GroupHeader(int iconRes, MapMarkersGroup group) { this.iconRes = iconRes; this.group = group; } @@ -20,7 +18,7 @@ public class GroupHeader { return iconRes; } - public ItineraryGroup getGroup() { + public MapMarkersGroup getGroup() { return group; } } diff --git a/OsmAnd/src/net/osmand/plus/mapmarkers/MapMarker.java b/OsmAnd/src/net/osmand/plus/mapmarkers/MapMarker.java index e1f2c413f1..498c36f93c 100644 --- a/OsmAnd/src/net/osmand/plus/mapmarkers/MapMarker.java +++ b/OsmAnd/src/net/osmand/plus/mapmarkers/MapMarker.java @@ -11,7 +11,6 @@ import net.osmand.data.LatLon; import net.osmand.data.LocationPoint; import net.osmand.data.PointDescription; import net.osmand.plus.R; -import net.osmand.plus.itinerary.ItineraryGroup; import net.osmand.util.Algorithms; import static net.osmand.data.PointDescription.POINT_TYPE_MAP_MARKER; @@ -47,8 +46,8 @@ public class MapMarker implements LocationPoint { public int getType() { return favouritePoint == null ? - (wptPt == null ? ItineraryGroup.ANY_TYPE : ItineraryGroup.GPX_TYPE) : - ItineraryGroup.FAVORITES_TYPE; + (wptPt == null ? MapMarkersGroup.ANY_TYPE : MapMarkersGroup.GPX_TYPE) : + MapMarkersGroup.FAVORITES_TYPE; } public PointDescription getPointDescription(Context ctx) { diff --git a/OsmAnd/src/net/osmand/plus/mapmarkers/MapMarkersDbHelper.java b/OsmAnd/src/net/osmand/plus/mapmarkers/MapMarkersDbHelper.java index 4fe3debbb5..0b2ba5ab0c 100644 --- a/OsmAnd/src/net/osmand/plus/mapmarkers/MapMarkersDbHelper.java +++ b/OsmAnd/src/net/osmand/plus/mapmarkers/MapMarkersDbHelper.java @@ -8,7 +8,6 @@ import net.osmand.plus.OsmandApplication; import net.osmand.plus.api.SQLiteAPI.SQLiteConnection; import net.osmand.plus.api.SQLiteAPI.SQLiteCursor; import net.osmand.plus.helpers.SearchHistoryHelper; -import net.osmand.plus.itinerary.ItineraryGroup; import net.osmand.util.Algorithms; import java.util.ArrayList; @@ -162,7 +161,7 @@ public class MapMarkersDbHelper { } } - public void addGroup(ItineraryGroup group) { + public void addGroup(MapMarkersGroup group) { SQLiteConnection db = openConnection(false); if (db != null) { try { @@ -174,15 +173,15 @@ public class MapMarkersDbHelper { } } - public Map getAllGroupsMap() { - Map res = new LinkedHashMap<>(); + public Map getAllGroupsMap() { + Map res = new LinkedHashMap<>(); SQLiteConnection db = openConnection(true); if (db != null) { try { SQLiteCursor query = db.rawQuery(GROUPS_TABLE_SELECT, null); if (query != null && query.moveToFirst()) { do { - ItineraryGroup group = readGroup(query); + MapMarkersGroup group = readGroup(query); res.put(group.getId(), group); } while (query.moveToNext()); } @@ -196,14 +195,14 @@ public class MapMarkersDbHelper { return res; } - private ItineraryGroup readGroup(SQLiteCursor query) { + private MapMarkersGroup readGroup(SQLiteCursor query) { String id = query.getString(0); String name = query.getString(1); int type = query.getInt(2); boolean disabled = query.getInt(3) == 1; String categories = query.getString(4); - ItineraryGroup res = new ItineraryGroup(id, name, type); + MapMarkersGroup res = new MapMarkersGroup(id, name, type); res.setDisabled(disabled); res.setWptCategories(categories == null ? null : Algorithms.decodeStringSet(categories)); diff --git a/OsmAnd/src/net/osmand/plus/mapmarkers/MapMarkersDialogFragment.java b/OsmAnd/src/net/osmand/plus/mapmarkers/MapMarkersDialogFragment.java index ad453d5fe5..f0b226dd1e 100644 --- a/OsmAnd/src/net/osmand/plus/mapmarkers/MapMarkersDialogFragment.java +++ b/OsmAnd/src/net/osmand/plus/mapmarkers/MapMarkersDialogFragment.java @@ -218,13 +218,13 @@ public class MapMarkersDialogFragment extends DialogFragment implements OnGroupS @Override public void onResume() { super.onResume(); - getMyApplication().getItineraryHelper().addSyncListener(this); + getMyApplication().getMapMarkersHelper().addSyncListener(this); } @Override public void onPause() { super.onPause(); - getMyApplication().getItineraryHelper().removeSyncListener(this); + getMyApplication().getMapMarkersHelper().removeSyncListener(this); } @Override diff --git a/OsmAnd/src/net/osmand/plus/itinerary/ItineraryGroup.java b/OsmAnd/src/net/osmand/plus/mapmarkers/MapMarkersGroup.java similarity index 84% rename from OsmAnd/src/net/osmand/plus/itinerary/ItineraryGroup.java rename to OsmAnd/src/net/osmand/plus/mapmarkers/MapMarkersGroup.java index 595315a028..35a4585501 100644 --- a/OsmAnd/src/net/osmand/plus/itinerary/ItineraryGroup.java +++ b/OsmAnd/src/net/osmand/plus/mapmarkers/MapMarkersGroup.java @@ -1,12 +1,8 @@ -package net.osmand.plus.itinerary; +package net.osmand.plus.mapmarkers; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import net.osmand.plus.mapmarkers.CategoriesSubHeader; -import net.osmand.plus.mapmarkers.GroupHeader; -import net.osmand.plus.mapmarkers.MapMarker; -import net.osmand.plus.mapmarkers.ShowHideHistoryButton; import net.osmand.plus.wikivoyage.data.TravelArticle; import net.osmand.util.Algorithms; @@ -14,7 +10,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Set; -public class ItineraryGroup { +public class MapMarkersGroup { public static final int ANY_TYPE = -1; public static final int FAVORITES_TYPE = 0; @@ -35,14 +31,13 @@ public class ItineraryGroup { private TravelArticle wikivoyageArticle; // TODO should be removed from this class: private GroupHeader header; - private CategoriesSubHeader categoriesSubHeader; private ShowHideHistoryButton showHideHistoryButton; - public ItineraryGroup() { + public MapMarkersGroup() { } - public ItineraryGroup(@NonNull String id, @NonNull String name, int type) { + public MapMarkersGroup(@NonNull String id, @NonNull String name, int type) { this.id = id; this.name = name; this.type = type; @@ -80,10 +75,6 @@ public class ItineraryGroup { this.header = header; } - public void setCategoriesSubHeader(CategoriesSubHeader categoriesSubHeader) { - this.categoriesSubHeader = categoriesSubHeader; - } - public void setShowHideHistoryButton(ShowHideHistoryButton showHideHistoryButton) { this.showHideHistoryButton = showHideHistoryButton; } @@ -148,10 +139,6 @@ public class ItineraryGroup { return header; } - public CategoriesSubHeader getCategoriesSubHeader() { - return categoriesSubHeader; - } - public ShowHideHistoryButton getShowHideHistoryButton() { return showHideHistoryButton; } diff --git a/OsmAnd/src/net/osmand/plus/mapmarkers/MapMarkersHelper.java b/OsmAnd/src/net/osmand/plus/mapmarkers/MapMarkersHelper.java index 1f2233b7c0..729a7f3cfd 100644 --- a/OsmAnd/src/net/osmand/plus/mapmarkers/MapMarkersHelper.java +++ b/OsmAnd/src/net/osmand/plus/mapmarkers/MapMarkersHelper.java @@ -1,5 +1,7 @@ package net.osmand.plus.mapmarkers; +import android.os.AsyncTask; + import androidx.annotation.IntDef; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -14,12 +16,17 @@ import net.osmand.PlatformUtil; import net.osmand.data.FavouritePoint; import net.osmand.data.LatLon; import net.osmand.data.PointDescription; +import net.osmand.plus.FavouritesDbHelper.FavoriteGroup; +import net.osmand.plus.GPXDatabase; import net.osmand.plus.GeocodingLookupService; import net.osmand.plus.GeocodingLookupService.AddressLookupRequest; +import net.osmand.plus.GpxSelectionHelper; +import net.osmand.plus.GpxSelectionHelper.SelectedGpxFile; import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; import net.osmand.plus.Version; -import net.osmand.plus.itinerary.ItineraryGroup; +import net.osmand.plus.wikivoyage.data.TravelArticle; +import net.osmand.plus.wikivoyage.data.TravelHelper; import net.osmand.util.Algorithms; import net.osmand.util.MapUtils; @@ -34,10 +41,15 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Date; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Locale; +import java.util.Map; +import java.util.Set; import java.util.TimeZone; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import static net.osmand.GPXUtilities.GPX_TIME_FORMAT; import static net.osmand.data.PointDescription.POINT_TYPE_MAP_MARKER; @@ -63,13 +75,17 @@ public class MapMarkersHelper { public @interface MapMarkersSortByDef { } - private OsmandApplication app; + private OsmandApplication ctx; private MapMarkersDbHelper markersDbHelper; + private ExecutorService executorService = Executors.newSingleThreadExecutor(); + private List mapMarkers = new ArrayList<>(); private List mapMarkersHistory = new ArrayList<>(); + private List mapMarkersGroups = new ArrayList<>(); private List listeners = new ArrayList<>(); + private Set syncListeners = new HashSet<>(); private MarkersPlanRouteContext planRouteContext; @@ -81,24 +97,29 @@ public class MapMarkersHelper { return mapMarkersHistory; } + public List getMapMarkersGroups() { + return mapMarkersGroups; + } + public boolean isStartFromMyLocation() { - return app.getSettings().ROUTE_MAP_MARKERS_START_MY_LOC.get(); + return ctx.getSettings().ROUTE_MAP_MARKERS_START_MY_LOC.get(); } public void setStartFromMyLocation(boolean startFromMyLocation) { - app.getSettings().ROUTE_MAP_MARKERS_START_MY_LOC.set(startFromMyLocation); + ctx.getSettings().ROUTE_MAP_MARKERS_START_MY_LOC.set(startFromMyLocation); } public MarkersPlanRouteContext getPlanRouteContext() { return planRouteContext; } - public MapMarkersHelper(OsmandApplication app) { - this.app = app; - markersDbHelper = app.getMapMarkersDbHelper(); - planRouteContext = new MarkersPlanRouteContext(app); + public MapMarkersHelper(OsmandApplication ctx) { + this.ctx = ctx; + markersDbHelper = ctx.getMapMarkersDbHelper(); + planRouteContext = new MarkersPlanRouteContext(ctx); markersDbHelper.removeDisabledGroups(); loadMarkers(); + loadGroups(); } private void loadMarkers() { @@ -112,11 +133,63 @@ public class MapMarkersHelper { sortMarkers(markersHistory, true, BY_DATE_ADDED_DESC); addToMapMarkersHistoryList(markersHistory); - if (!app.isApplicationInitializing()) { + if (!ctx.isApplicationInitializing()) { lookupAddressAll(); } } + private void loadGroups() { + Map groupsMap = markersDbHelper.getAllGroupsMap(); + List allMarkers = new ArrayList<>(mapMarkers); + allMarkers.addAll(mapMarkersHistory); + + Iterator> iterator = groupsMap.entrySet().iterator(); + while (iterator.hasNext()) { + MapMarkersGroup group = iterator.next().getValue(); + if (group.getType() == MapMarkersGroup.GPX_TYPE && !new File(group.getId()).exists()) { + markersDbHelper.removeMarkersGroup(group.getId()); + iterator.remove(); + } + } + + MapMarkersGroup noGroup = null; + + for (MapMarker marker : allMarkers) { + MapMarkersGroup group = groupsMap.get(marker.groupKey); + if (group == null) { + if (noGroup == null) { + noGroup = new MapMarkersGroup(); + noGroup.setCreationDate(Long.MAX_VALUE); + } + noGroup.getMarkers().add(marker); + } else { + if (marker.creationDate < group.getCreationDate()) { + group.setCreationDate(marker.creationDate); + } + group.getMarkers().add(marker); + } + } + + mapMarkersGroups = new ArrayList<>(groupsMap.values()); + if (noGroup != null) { + sortMarkers(noGroup.getMarkers(), false, BY_DATE_ADDED_DESC); + addToGroupsList(noGroup); + } + + sortGroups(); + + for (MapMarkersGroup group : mapMarkersGroups) { + updateGroup(group); + } + } + + public void syncAllGroupsAsync() { + for (MapMarkersGroup gr : mapMarkersGroups) { + if (gr.getId() != null && gr.getName() != null) { + runSynchronization(gr); + } + } + } public void lookupAddressAll() { for (MapMarker mapMarker : mapMarkers) { @@ -128,7 +201,7 @@ public class MapMarkersHelper { } private void lookupAddress(final MapMarker mapMarker) { - if (mapMarker != null && mapMarker.getOriginalPointDescription().isSearchingAddress(app)) { + if (mapMarker != null && mapMarker.getOriginalPointDescription().isSearchingAddress(ctx)) { cancelPointAddressRequests(mapMarker.point); AddressLookupRequest lookupRequest = new AddressLookupRequest(mapMarker.point, new GeocodingLookupService.OnAddressLookupResult() { @@ -136,7 +209,7 @@ public class MapMarkersHelper { public void geocodingDone(String address) { PointDescription pointDescription = mapMarker.getOriginalPointDescription(); if (Algorithms.isEmpty(address)) { - pointDescription.setName(PointDescription.getAddressNotFoundStr(app)); + pointDescription.setName(PointDescription.getAddressNotFoundStr(ctx)); } else { pointDescription.setName(address); } @@ -144,7 +217,7 @@ public class MapMarkersHelper { refreshMarker(mapMarker); } }, null); - app.getGeocodingLookupService().lookupAddress(lookupRequest); + ctx.getGeocodingLookupService().lookupAddress(lookupRequest); } } @@ -161,7 +234,7 @@ public class MapMarkersHelper { private void cancelPointAddressRequests(LatLon latLon) { if (latLon != null) { - app.getGeocodingLookupService().cancel(latLon); + ctx.getGeocodingLookupService().cancel(latLon); } } @@ -190,7 +263,7 @@ public class MapMarkersHelper { reorderActiveMarkersIfNeeded(); } - public void sortMarkers(List markers, final boolean visited, final @MapMarkersSortByDef int sortByMode) { + private void sortMarkers(List markers, final boolean visited, final @MapMarkersSortByDef int sortByMode) { sortMarkers(markers, visited, sortByMode, null); } @@ -222,15 +295,92 @@ public class MapMarkersHelper { return sortByMode == BY_DISTANCE_DESC ? 1 : -1; } } else { - String n1 = mapMarker1.getName(app); - String n2 = mapMarker2.getName(app); + String n1 = mapMarker1.getName(ctx); + String n2 = mapMarker2.getName(ctx); return n1.compareToIgnoreCase(n2); } } }); } - public void addHistoryMarkersToGroup(@NonNull ItineraryGroup group) { + public void runSynchronization(final @NonNull MapMarkersGroup group) { + ctx.runInUIThread(new Runnable() { + @Override + public void run() { + new SyncGroupTask(group).executeOnExecutor(executorService); + } + }); + } + + public MapMarkersGroup getMarkersGroup(GPXFile gpx) { + if (gpx == null || gpx.path == null) { + return null; + } + return getMapMarkerGroupById(getMarkerGroupId(new File(gpx.path)), MapMarkersGroup.GPX_TYPE); + } + + public MapMarkersGroup getMarkersGroup(FavoriteGroup favGroup) { + return getMapMarkerGroupById(getMarkerGroupId(favGroup), MapMarkersGroup.FAVORITES_TYPE); + } + + public MapMarkersGroup addOrEnableGpxGroup(@NonNull File file) { + updateGpxShowAsMarkers(file); + MapMarkersGroup gr = getMapMarkerGroupById(getMarkerGroupId(file), MapMarkersGroup.GPX_TYPE); + if (gr == null) { + gr = createGPXMarkerGroup(file); + addGroupInternally(gr); + } + enableGroup(gr); + return gr; + } + + public MapMarkersGroup addOrEnableGroup(@NonNull GPXFile file) { + updateGpxShowAsMarkers(new File(file.path)); + MapMarkersGroup gr = getMarkersGroup(file); + if (gr == null) { + gr = createGPXMarkerGroup(new File(file.path)); + addGroupInternally(gr); + } + enableGroup(gr); + return gr; + } + + public MapMarkersGroup addOrEnableGroup(@NonNull FavoriteGroup group) { + MapMarkersGroup gr = getMarkersGroup(group); + if (gr == null) { + gr = createFavMarkerGroup(group); + addGroupInternally(gr); + } + enableGroup(gr); + return gr; + } + + public void enableGroup(@NonNull MapMarkersGroup gr) { + // check if group doesn't exist internally + if (!mapMarkersGroups.contains(gr)) { + addGroupInternally(gr); + } + if (gr.isDisabled()) { + updateGroupDisabled(gr, false); + } + runSynchronization(gr); + } + + private void addGroupInternally(MapMarkersGroup gr) { + markersDbHelper.addGroup(gr); + addHistoryMarkersToGroup(gr); + addToGroupsList(gr); + } + + private void updateGpxShowAsMarkers(File file) { + GPXDatabase.GpxDataItem dataItem = ctx.getGpxDbHelper().getItem(file); + if (dataItem != null) { + ctx.getGpxDbHelper().updateShowAsMarkers(dataItem, true); + dataItem.setShowAsMarkers(true); + } + } + + private void addHistoryMarkersToGroup(@NonNull MapMarkersGroup group) { List historyMarkers = new ArrayList<>(mapMarkersHistory); for (MapMarker m : historyMarkers) { if (m.groupKey != null && group.getId() != null && m.groupKey.equals(group.getId())) { @@ -239,15 +389,33 @@ public class MapMarkersHelper { } } - public void removeMarkersGroup(ItineraryGroup group) { + public void removeMarkersGroup(MapMarkersGroup group) { if (group != null) { markersDbHelper.removeMarkersGroup(group.getId()); removeGroupActiveMarkers(group, false); - app.getItineraryHelper().removeFromGroupsList(group); + removeFromGroupsList(group); } } - public void removeGroupActiveMarkers(ItineraryGroup group, boolean updateGroup) { + public void updateGroupDisabled(@NonNull MapMarkersGroup group, boolean disabled) { + String id = group.getId(); + if (id != null) { + markersDbHelper.updateGroupDisabled(id, disabled); + group.setDisabled(disabled); + } + } + + public void updateGroupWptCategories(@NonNull MapMarkersGroup group, Set wptCategories) { + String id = group.getId(); + if (id != null) { + group.setWptCategories(wptCategories); + if (wptCategories != null) { + markersDbHelper.updateGroupCategories(id, group.getWptCategoriesString()); + } + } + } + + private void removeGroupActiveMarkers(MapMarkersGroup group, boolean updateGroup) { if (group != null) { markersDbHelper.removeActiveMarkersFromGroup(group.getId()); removeFromMapMarkersList(group.getActiveMarkers()); @@ -260,21 +428,27 @@ public class MapMarkersHelper { } } - public void updateGroup(ItineraryGroup itineraryGroup) { - if (itineraryGroup.getId() == null || itineraryGroup.getName() == null) { + public void updateGroups() { + for (MapMarkersGroup group : mapMarkersGroups) { + updateGroup(group); + } + } + + public void updateGroup(MapMarkersGroup mapMarkersGroup) { + if (mapMarkersGroup.getId() == null || mapMarkersGroup.getName() == null) { return; } - createHeadersInGroup(itineraryGroup); - int historyMarkersCount = itineraryGroup.getHistoryMarkers().size(); - ShowHideHistoryButton showHideHistoryButton = itineraryGroup.getShowHideHistoryButton(); + createHeadersInGroup(mapMarkersGroup); + int historyMarkersCount = mapMarkersGroup.getHistoryMarkers().size(); + ShowHideHistoryButton showHideHistoryButton = mapMarkersGroup.getShowHideHistoryButton(); if (showHideHistoryButton != null) { if (historyMarkersCount == 0) { - itineraryGroup.setShowHideHistoryButton(null); + mapMarkersGroup.setShowHideHistoryButton(null); } } else if (historyMarkersCount > 0) { showHideHistoryButton = new ShowHideHistoryButton(); showHideHistoryButton.showHistory = false; - itineraryGroup.setShowHideHistoryButton(showHideHistoryButton); + mapMarkersGroup.setShowHideHistoryButton(showHideHistoryButton); } } @@ -286,38 +460,124 @@ public class MapMarkersHelper { private void addMarkerToGroup(MapMarker marker) { if (marker != null) { - ItineraryGroup itineraryGroup = app.getItineraryHelper().getMapMarkerGroupById(marker.groupKey, marker.getType()); - if (itineraryGroup != null) { - itineraryGroup.getMarkers().add(marker); - updateGroup(itineraryGroup); - if (itineraryGroup.getName() == null) { - sortMarkers(itineraryGroup.getMarkers(), false, BY_DATE_ADDED_DESC); + MapMarkersGroup mapMarkersGroup = getMapMarkerGroupById(marker.groupKey, marker.getType()); + if (mapMarkersGroup != null) { + mapMarkersGroup.getMarkers().add(marker); + updateGroup(mapMarkersGroup); + if (mapMarkersGroup.getName() == null) { + sortMarkers(mapMarkersGroup.getMarkers(), false, BY_DATE_ADDED_DESC); } } else { - itineraryGroup = new ItineraryGroup(marker.groupKey, marker.groupName, ItineraryGroup.ANY_TYPE); - itineraryGroup.setCreationDate(Long.MAX_VALUE); - itineraryGroup.getMarkers().add(marker); - app.getItineraryHelper().addToGroupsList(itineraryGroup); - app.getItineraryHelper().sortGroups(); - updateGroup(itineraryGroup); + mapMarkersGroup = new MapMarkersGroup(marker.groupKey, marker.groupName, MapMarkersGroup.ANY_TYPE); + mapMarkersGroup.setCreationDate(Long.MAX_VALUE); + mapMarkersGroup.getMarkers().add(marker); + addToGroupsList(mapMarkersGroup); + sortGroups(); + updateGroup(mapMarkersGroup); } } } - public void createHeadersInGroup(@NonNull ItineraryGroup group) { + private void createHeadersInGroup(@NonNull MapMarkersGroup group) { int type = group.getType(); int headerIconId = 0; - int subHeaderIconId = 0; if (type != -1) { - headerIconId = type == ItineraryGroup.FAVORITES_TYPE + headerIconId = type == MapMarkersGroup.FAVORITES_TYPE ? R.drawable.ic_action_favorite : R.drawable.ic_action_polygom_dark; - subHeaderIconId = R.drawable.ic_action_filter; } GroupHeader header = new GroupHeader(headerIconId, group); - CategoriesSubHeader categoriesSubHeader = new CategoriesSubHeader(subHeaderIconId, group); - group.setHeader(header); - group.setCategoriesSubHeader(categoriesSubHeader); + } + + private void removeMarkerFromGroup(MapMarker marker) { + if (marker != null) { + MapMarkersGroup mapMarkersGroup = getMapMarkerGroupById(marker.groupKey, marker.getType()); + if (mapMarkersGroup != null) { + mapMarkersGroup.getMarkers().remove(marker); + updateGroup(mapMarkersGroup); + } + } + } + + private void sortGroups() { + if (mapMarkersGroups.size() > 0) { + Collections.sort(mapMarkersGroups, new Comparator() { + @Override + public int compare(MapMarkersGroup group1, MapMarkersGroup group2) { + long t1 = group1.getCreationDate(); + long t2 = group2.getCreationDate(); + return (t1 > t2) ? -1 : ((t1 == t2) ? 0 : 1); + } + }); + } + } + + @Nullable + public MapMarkersGroup getMapMarkerGroupById(String id, int type) { + for (MapMarkersGroup group : mapMarkersGroups) { + if ((id == null && group.getId() == null) + || (group.getId() != null && group.getId().equals(id))) { + if (type == MapMarkersGroup.ANY_TYPE || type == group.getType()) { + return group; + } + } + } + return null; + } + + private MapMarkersGroup createGPXMarkerGroup(File fl) { + return new MapMarkersGroup(getMarkerGroupId(fl), + Algorithms.getFileNameWithoutExtension(fl.getName()), + MapMarkersGroup.GPX_TYPE); + } + + private MapMarkersGroup createFavMarkerGroup(FavoriteGroup favGroup) { + return new MapMarkersGroup(favGroup.getName(), favGroup.getName(), MapMarkersGroup.FAVORITES_TYPE); + } + + private String getMarkerGroupId(File gpx) { + return gpx.getAbsolutePath(); + } + + private String getMarkerGroupId(FavoriteGroup group) { + return group.getName(); + } + + @NonNull + public List getGroupsForDisplayedGpx() { + List res = new ArrayList<>(); + List selectedGpxFiles = ctx.getSelectedGpxHelper().getSelectedGPXFiles(); + for (SelectedGpxFile selected : selectedGpxFiles) { + MapMarkersGroup search = getMarkersGroup(selected.getGpxFile()); + if (search == null && selected.getGpxFile() != null && selected.getGpxFile().path != null) { + MapMarkersGroup group = createGPXMarkerGroup(new File(selected.getGpxFile().path)); + group.setDisabled(true); + createHeadersInGroup(group); + res.add(group); + } + } + return res; + } + + @NonNull + public List getGroupsForSavedArticlesTravelBook() { + List res = new ArrayList<>(); + TravelHelper travelHelper = ctx.getTravelHelper(); + if (travelHelper.isAnyTravelBookPresent()) { + List savedArticles = travelHelper.getBookmarksHelper().getSavedArticles(); + for (TravelArticle art : savedArticles) { + String gpxName = travelHelper.getGPXName(art); + File path = ctx.getAppPath(IndexConstants.GPX_TRAVEL_DIR + gpxName); + MapMarkersGroup search = getMapMarkerGroupById(getMarkerGroupId(path), MapMarkersGroup.GPX_TYPE); + if (search == null) { + MapMarkersGroup group = createGPXMarkerGroup(path); + group.setDisabled(true); + createHeadersInGroup(group); + res.add(group); + } + } + } + return res; } @Nullable @@ -352,7 +612,7 @@ public class MapMarkersHelper { private List getMarkers() { List res = new ArrayList<>(mapMarkers); - if (app.getSettings().KEEP_PASSED_MARKERS_ON_MAP.get()) { + if (ctx.getSettings().KEEP_PASSED_MARKERS_ON_MAP.get()) { res.addAll(mapMarkersHistory); } return res; @@ -371,12 +631,12 @@ public class MapMarkersHelper { return null; } - public void addNewMarkerIfNeeded(@NonNull ItineraryGroup group, - @NonNull List groupMarkers, - @NonNull LatLon latLon, - @NonNull String name, - @Nullable FavouritePoint favouritePoint, - @Nullable WptPt wptPt) { + private void addNewMarkerIfNeeded(@NonNull MapMarkersGroup group, + @NonNull List groupMarkers, + @NonNull LatLon latLon, + @NonNull String name, + @Nullable FavouritePoint favouritePoint, + @Nullable WptPt wptPt) { boolean exists = false; Iterator iterator = groupMarkers.iterator(); @@ -406,7 +666,7 @@ public class MapMarkersHelper { } } - public void removeOldMarkersIfPresent(List markers) { + private void removeOldMarkersIfPresent(List markers) { if (!markers.isEmpty()) { boolean needRefresh = false; for (MapMarker marker : markers) { @@ -473,7 +733,7 @@ public class MapMarkersHelper { addToMapMarkersList(markers); reorderActiveMarkersIfNeeded(); sortMarkers(mapMarkersHistory, true, BY_DATE_ADDED_DESC); - app.getItineraryHelper().updateGroups(); + updateGroups(); refresh(); } } @@ -490,7 +750,7 @@ public class MapMarkersHelper { } else { removeFromMapMarkersList(marker); } - app.getItineraryHelper().removeMarkerFromGroup(marker); + removeMarkerFromGroup(marker); if (refresh) { refresh(); } @@ -587,7 +847,7 @@ public class MapMarkersHelper { addToMapMarkersHistoryList(mapMarkers); mapMarkers = new ArrayList<>(); sortMarkers(mapMarkersHistory, true, BY_DATE_ADDED_DESC); - app.getItineraryHelper().updateGroups(); + updateGroups(); refresh(); } @@ -608,18 +868,18 @@ public class MapMarkersHelper { public void addMapMarkers(@NonNull List points, @NonNull List historyNames, - @Nullable ItineraryGroup group) { + @Nullable MapMarkersGroup group) { addMarkers(points, historyNames, group, null, null, null); } private void addMarkers(@NonNull List points, @NonNull List historyNames, - @Nullable ItineraryGroup group, + @Nullable MapMarkersGroup group, @Nullable List favouritePoints, @Nullable List wptPts, @Nullable List mapObjNames) { if (points.size() > 0) { - app.getSettings().SHOW_MAP_MARKERS.set(true); + ctx.getSettings().SHOW_MAP_MARKERS.set(true); int colorIndex = -1; List addedMarkers = new ArrayList<>(); for (int i = 0; i < points.size(); i++) { @@ -635,7 +895,7 @@ public class MapMarkersHelper { pointDescription = historyName; } if (pointDescription.isLocation() && Algorithms.isEmpty(pointDescription.getName())) { - pointDescription.setName(PointDescription.getSearchAddressStr(app)); + pointDescription.setName(PointDescription.getSearchAddressStr(ctx)); } if (colorIndex == -1) { if (mapMarkers.size() > 0) { @@ -649,7 +909,7 @@ public class MapMarkersHelper { MapMarker marker = new MapMarker(point, pointDescription, colorIndex, false, 0); if (group != null) { - marker.id = group.getId() + marker.getName(app) + MapUtils.createShortLinkString(marker.point.getLatitude(), marker.point.getLongitude(), 15); + marker.id = group.getId() + marker.getName(ctx) + MapUtils.createShortLinkString(marker.point.getLatitude(), marker.point.getLongitude(), 15); if (markersDbHelper.getMarker(marker.id) != null) { continue; } @@ -705,6 +965,14 @@ public class MapMarkersHelper { } } + public void addSyncListener(OnGroupSyncedListener listener) { + syncListeners.add(listener); + } + + public void removeSyncListener(OnGroupSyncedListener listener) { + syncListeners.remove(listener); + } + public void addListener(MapMarkerChangedListener l) { if (!listeners.contains(l)) { listeners.add(l); @@ -716,7 +984,7 @@ public class MapMarkersHelper { } private void refreshMarker(final MapMarker marker) { - app.runInUIThread(new Runnable() { + ctx.runInUIThread(new Runnable() { @Override public void run() { for (MapMarkerChangedListener l : listeners) { @@ -727,7 +995,7 @@ public class MapMarkersHelper { } private void refresh() { - app.runInUIThread(new Runnable() { + ctx.runInUIThread(new Runnable() { @Override public void run() { for (MapMarkerChangedListener l : listeners) { @@ -737,14 +1005,28 @@ public class MapMarkersHelper { }); } + public List getMapMarkersFromDefaultGroups(boolean history) { + List mapMarkers = new ArrayList<>(); + for (MapMarkersGroup group : mapMarkersGroups) { + if (group.getType() == MapMarkersGroup.ANY_TYPE) { + for (MapMarker marker : group.getMarkers()) { + if (history && marker.history || !history && !marker.history) { + mapMarkers.add(marker); + } + } + } + } + return mapMarkers; + } + public String saveMarkersToFile(String fileName) { GPXFile gpxFile = generateGpx(); String dirName = IndexConstants.GPX_INDEX_DIR + IndexConstants.MAP_MARKERS_INDEX_DIR; - File dir = app.getAppPath(dirName); + File dir = ctx.getAppPath(dirName); if (!dir.exists()) { dir.mkdirs(); } - String uniqueFileName = FileUtils.createUniqueFileName(app, fileName, dirName, IndexConstants.GPX_FILE_EXT); + String uniqueFileName = FileUtils.createUniqueFileName(ctx, fileName, dirName, IndexConstants.GPX_FILE_EXT); File fout = new File(dir, uniqueFileName + IndexConstants.GPX_FILE_EXT); GPXUtilities.writeGpxFile(fout, gpxFile); @@ -759,13 +1041,13 @@ public class MapMarkersHelper { SimpleDateFormat format = new SimpleDateFormat(GPX_TIME_FORMAT, Locale.US); format.setTimeZone(TimeZone.getTimeZone("UTC")); - GPXFile gpxFile = new GPXFile(Version.getFullVersion(app)); + GPXFile gpxFile = new GPXFile(Version.getFullVersion(ctx)); for (MapMarker marker : markers) { WptPt wpt = new WptPt(); wpt.lat = marker.getLatitude(); wpt.lon = marker.getLongitude(); wpt.name = marker.getOnlyName(); - wpt.setColor(ContextCompat.getColor(app, MapMarker.getColorId(marker.colorIndex))); + wpt.setColor(ContextCompat.getColor(ctx, MapMarker.getColorId(marker.colorIndex))); if (completeBackup) { if (marker.creationDate != 0) { wpt.getExtensionsToWrite().put(CREATION_DATE, format.format(new Date(marker.creationDate))); @@ -786,7 +1068,7 @@ public class MapMarkersHelper { List mapMarkers = new ArrayList<>(); for (WptPt point : gpxFile.getPoints()) { LatLon latLon = new LatLon(point.lat, point.lon); - int colorIndex = MapMarker.getColorIndex(app, point.getColor()); + int colorIndex = MapMarker.getColorIndex(ctx, point.getColor()); PointDescription name = new PointDescription(PointDescription.POINT_TYPE_LOCATION, point.name); MapMarker marker = new MapMarker(latLon, name, colorIndex, false, 0); @@ -877,6 +1159,22 @@ public class MapMarkersHelper { mapMarkersHistory = copyList; } + // accessors to markers groups: + + private void addToGroupsList(MapMarkersGroup group) { + List copyList = new ArrayList<>(mapMarkersGroups); + copyList.add(group); + mapMarkersGroups = copyList; + } + + private void removeFromGroupsList(MapMarkersGroup group) { + List copyList = new ArrayList<>(mapMarkersGroups); + copyList.remove(group); + mapMarkersGroups = copyList; + } + + // --------------------------------------------------------------------------------------------- + // classes and interfaces: public interface MapMarkerChangedListener { @@ -891,4 +1189,93 @@ public class MapMarkersHelper { void onSyncDone(); } + + private class SyncGroupTask extends AsyncTask { + + private MapMarkersGroup group; + + SyncGroupTask(MapMarkersGroup group) { + this.group = group; + } + + @Override + protected void onPreExecute() { + if (!syncListeners.isEmpty()) { + ctx.runInUIThread(new Runnable() { + @Override + public void run() { + for (OnGroupSyncedListener listener : syncListeners) { + listener.onSyncStarted(); + } + } + }); + } + } + + @Override + protected Void doInBackground(Void... voids) { + runGroupSynchronization(); + return null; + } + + // TODO extract method from Asynctask to Helper directly + private void runGroupSynchronization() { + List groupMarkers = new ArrayList<>(group.getMarkers()); + if (group.getType() == MapMarkersGroup.FAVORITES_TYPE) { + FavoriteGroup favGroup = ctx.getFavorites().getGroup(group.getName()); + if (favGroup == null) { + return; + } + group.setVisible(favGroup.isVisible()); + if (!group.isVisible() || group.isDisabled()) { + removeGroupActiveMarkers(group, true); + return; + } + List points = new ArrayList<>(favGroup.getPoints()); + for (FavouritePoint fp : points) { + addNewMarkerIfNeeded(group, groupMarkers, new LatLon(fp.getLatitude(), fp.getLongitude()), fp.getName(), fp, null); + } + } else if (group.getType() == MapMarkersGroup.GPX_TYPE) { + GpxSelectionHelper gpxHelper = ctx.getSelectedGpxHelper(); + File file = new File(group.getId()); + if (!file.exists()) { + return; + } + + String gpxPath = group.getId(); + SelectedGpxFile selectedGpxFile = gpxHelper.getSelectedFileByPath(gpxPath); + GPXFile gpx = selectedGpxFile == null ? null : selectedGpxFile.getGpxFile(); + group.setVisible(gpx != null || group.isVisibleUntilRestart()); + if (gpx == null || group.isDisabled()) { + removeGroupActiveMarkers(group, true); + return; + } + + boolean addAll = group.getWptCategories() == null || group.getWptCategories().isEmpty(); + List gpxPoints = new ArrayList<>(gpx.getPoints()); + for (WptPt pt : gpxPoints) { + if (addAll || group.getWptCategories().contains(pt.category) + || (pt.category == null && group.getWptCategories().contains(""))) { + addNewMarkerIfNeeded(group, groupMarkers, new LatLon(pt.lat, pt.lon), pt.name, null, pt); + } + } + } + + removeOldMarkersIfPresent(groupMarkers); + } + + @Override + protected void onPostExecute(Void aVoid) { + if (!syncListeners.isEmpty()) { + ctx.runInUIThread(new Runnable() { + @Override + public void run() { + for (OnGroupSyncedListener listener : syncListeners) { + listener.onSyncDone(); + } + } + }); + } + } + } } \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/mapmarkers/SelectWptCategoriesBottomSheetDialogFragment.java b/OsmAnd/src/net/osmand/plus/mapmarkers/SelectWptCategoriesBottomSheetDialogFragment.java index 2cabb6f804..08db4d40fc 100644 --- a/OsmAnd/src/net/osmand/plus/mapmarkers/SelectWptCategoriesBottomSheetDialogFragment.java +++ b/OsmAnd/src/net/osmand/plus/mapmarkers/SelectWptCategoriesBottomSheetDialogFragment.java @@ -20,8 +20,6 @@ import net.osmand.plus.base.bottomsheetmenu.BottomSheetItemWithCompoundButton; import net.osmand.plus.base.bottomsheetmenu.simpleitems.DividerItem; import net.osmand.plus.base.bottomsheetmenu.simpleitems.ShortDescriptionItem; import net.osmand.plus.base.bottomsheetmenu.simpleitems.TitleItem; -import net.osmand.plus.itinerary.ItineraryGroup; -import net.osmand.plus.itinerary.ItineraryHelper; import java.io.File; import java.util.ArrayList; @@ -138,18 +136,18 @@ public class SelectWptCategoriesBottomSheetDialogFragment extends MenuBottomShee private void updateAddOrEnableGroupWptCategories() { OsmandApplication app = getMyApplication(); GpxSelectionHelper gpxSelectionHelper = app.getSelectedGpxHelper(); - ItineraryHelper helper = app.getItineraryHelper(); + MapMarkersHelper mapMarkersHelper = app.getMapMarkersHelper(); SelectedGpxFile selectedGpxFile = gpxSelectionHelper.getSelectedFileByPath(gpxFile.path); if (selectedGpxFile == null) { gpxSelectionHelper.selectGpxFile(gpxFile, true, false, false, false, false); } - ItineraryGroup group = helper.getMarkersGroup(gpxFile); + MapMarkersGroup group = mapMarkersHelper.getMarkersGroup(gpxFile); if (group == null) { - group = helper.addOrEnableGroup(gpxFile); + group = mapMarkersHelper.addOrEnableGroup(gpxFile); } - helper.updateGroupWptCategories(group, selectedCategories); - helper.runSynchronization(group); + mapMarkersHelper.updateGroupWptCategories(group, selectedCategories); + mapMarkersHelper.runSynchronization(group); } private boolean isAllChecked() { diff --git a/OsmAnd/src/net/osmand/plus/mapmarkers/adapters/MapMarkersActiveAdapter.java b/OsmAnd/src/net/osmand/plus/mapmarkers/adapters/MapMarkersActiveAdapter.java index 4dfe175720..2fa206d08f 100644 --- a/OsmAnd/src/net/osmand/plus/mapmarkers/adapters/MapMarkersActiveAdapter.java +++ b/OsmAnd/src/net/osmand/plus/mapmarkers/adapters/MapMarkersActiveAdapter.java @@ -14,7 +14,7 @@ import com.google.android.material.snackbar.Snackbar; import net.osmand.data.LatLon; import net.osmand.plus.mapmarkers.MapMarker; -import net.osmand.plus.itinerary.ItineraryGroup; +import net.osmand.plus.mapmarkers.MapMarkersGroup; import net.osmand.plus.OsmAndFormatter; import net.osmand.plus.R; import net.osmand.plus.UiUtilities; @@ -214,8 +214,8 @@ public class MapMarkersActiveAdapter extends RecyclerView.Adapter(); - ItineraryHelper helper = app.getItineraryHelper(); + MapMarkersHelper helper = app.getMapMarkersHelper(); helper.updateGroups(); - List groups = new ArrayList<>(helper.getItineraryGroups()); + List groups = new ArrayList<>(helper.getMapMarkersGroups()); groups.addAll(helper.getGroupsForDisplayedGpx()); groups.addAll(helper.getGroupsForSavedArticlesTravelBook()); + + // evaluate time constants + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); + long currentTimeMillis = System.currentTimeMillis(); + Calendar currentDateCalendar = Calendar.getInstance(); + currentDateCalendar.setTimeInMillis(currentTimeMillis); + // evaluate today, yesterday, last 7 days + String today = dateFormat.format(currentDateCalendar.getTime()); + currentDateCalendar.add(Calendar.DAY_OF_YEAR, -1); + String yesterday = dateFormat.format(currentDateCalendar.getTime()); + currentDateCalendar.set(Calendar.HOUR_OF_DAY, 0); + currentDateCalendar.set(Calendar.MINUTE, 0); + currentDateCalendar.add(Calendar.DAY_OF_YEAR, -6); + long last7Days = currentDateCalendar.getTimeInMillis(); + // evaluate this year & last 3 months + currentDateCalendar.setTimeInMillis(currentTimeMillis); + String thisYear = dateFormat.format(currentDateCalendar.getTime()).substring(0, 5); + currentDateCalendar.add(Calendar.MONTH, -1); + String monthMinus1 = dateFormat.format(currentDateCalendar.getTime()).substring(0, 8); + currentDateCalendar.add(Calendar.MONTH, -1); + String monthMinus2 = dateFormat.format(currentDateCalendar.getTime()).substring(0, 8); + currentDateCalendar.add(Calendar.MONTH, -1); + String monthMinus3 = dateFormat.format(currentDateCalendar.getTime()).substring(0, 8); + + + Calendar markerCalendar = Calendar.getInstance(); + for (int i = 0; i < groups.size(); i++) { - ItineraryGroup group = groups.get(i); + MapMarkersGroup group = groups.get(i); if (!group.isVisible()) { continue; } String markerGroupName = group.getName(); - if (markerGroupName == null) { - int previousDateHeader = -1; - int monthsDisplayed = 0; - Calendar currentDateCalendar = Calendar.getInstance(); - currentDateCalendar.setTimeInMillis(System.currentTimeMillis()); - int currentDay = currentDateCalendar.get(Calendar.DAY_OF_YEAR); - int currentMonth = currentDateCalendar.get(Calendar.MONTH); - int currentYear = currentDateCalendar.get(Calendar.YEAR); - Calendar markerCalendar = Calendar.getInstance(); + if (markerGroupName == null) { + int previousGroupDateId = 0; + int monthsDisplayed = 0; List groupMarkers = group.getActiveMarkers(); for (int j = 0; j < groupMarkers.size(); j++) { MapMarker marker = groupMarkers.get(j); - markerCalendar.setTimeInMillis(marker.creationDate); - int markerDay = markerCalendar.get(Calendar.DAY_OF_YEAR); - int markerMonth = markerCalendar.get(Calendar.MONTH); - int markerYear = markerCalendar.get(Calendar.YEAR); - if (markerYear == currentYear) { - if (markerDay == currentDay && previousDateHeader != TODAY_HEADER) { - items.add(TODAY_HEADER); - previousDateHeader = TODAY_HEADER; - } else if (markerDay == currentDay - 1 && previousDateHeader != YESTERDAY_HEADER) { - items.add(YESTERDAY_HEADER); - previousDateHeader = YESTERDAY_HEADER; - } else if (currentDay - markerDay >= 2 && currentDay - markerDay <= 8 && previousDateHeader != LAST_SEVEN_DAYS_HEADER) { - items.add(LAST_SEVEN_DAYS_HEADER); - previousDateHeader = LAST_SEVEN_DAYS_HEADER; - } else if (currentDay - markerDay > 8 && monthsDisplayed < 3 && previousDateHeader != markerMonth) { - items.add(markerMonth); - previousDateHeader = markerMonth; - monthsDisplayed += 1; - } else if (currentMonth - markerMonth >= 4 && previousDateHeader != markerMonth && previousDateHeader != THIS_YEAR_HEADER) { - items.add(THIS_YEAR_HEADER); - previousDateHeader = THIS_YEAR_HEADER; + String markerDate = dateFormat.format(new Date(marker.creationDate)); + int currentGroupDateId; + MarkerGroupItem currentGroupItem = null ; + if (marker.creationDate >= currentTimeMillis || (today.equals(markerDate))) { + currentGroupDateId = -1; + currentGroupItem = MarkerGroupItem.TODAY_HEADER; + } else if (yesterday.equals(markerDate)) { + currentGroupDateId = -2; + currentGroupItem = MarkerGroupItem.YESTERDAY_HEADER; + } else if (marker.creationDate >= last7Days) { + currentGroupDateId = -3; + currentGroupItem = MarkerGroupItem.LAST_SEVEN_DAYS_HEADER; + } else if (markerDate.startsWith(monthMinus1)) { + currentGroupDateId = -5; + } else if (markerDate.startsWith(monthMinus2)) { + currentGroupDateId = -6; + } else if (markerDate.startsWith(monthMinus3)) { + currentGroupDateId = -7; + } else if (markerDate.startsWith(thisYear)) { + currentGroupItem = MarkerGroupItem.THIS_YEAR_HEADER; + currentGroupDateId = -4; + } else { + markerCalendar.setTimeInMillis(marker.creationDate); + currentGroupDateId = markerCalendar.get(Calendar.YEAR); + } + if (previousGroupDateId != currentGroupDateId) { + if (currentGroupItem != null) { + items.add(currentGroupItem); + } else if(currentGroupDateId < 0) { + SimpleDateFormat monthdateFormat = new SimpleDateFormat("LLLL", Locale.getDefault()); + String monthStr = monthdateFormat.format(new Date(marker.creationDate)); + if (monthStr.length() > 1) { + monthStr = Algorithms.capitalizeFirstLetter(monthStr); + } + items.add(new MarkerGroupItem(monthStr)); + } else { + items.add(new MarkerGroupItem(currentGroupDateId + "")); } - } else if (previousDateHeader != markerYear) { - items.add(markerYear); - previousDateHeader = markerYear; + previousGroupDateId = currentGroupDateId; } items.add(marker); } @@ -161,7 +193,7 @@ public class MapMarkersGroupsAdapter extends RecyclerView.Adapter categories = group.getWptCategories(); if (categories != null && !categories.isEmpty()) { @@ -617,17 +636,46 @@ public class MapMarkersGroupsAdapter extends RecyclerView.Adapter 1) { - monthStr = Character.toUpperCase(monthStr.charAt(0)) + monthStr.substring(1); + + public static class MarkerGroupItem { + + static final MarkerGroupItem TODAY_HEADER = new MarkerGroupItem(R.string.today); + static final MarkerGroupItem YESTERDAY_HEADER = new MarkerGroupItem(R.string.yesterday); + static final MarkerGroupItem LAST_SEVEN_DAYS_HEADER = new MarkerGroupItem(R.string.last_seven_days); + static final MarkerGroupItem THIS_YEAR_HEADER = new MarkerGroupItem(R.string.this_year); + private @StringRes int iname; + protected String name; + + public MarkerGroupItem(@StringRes int name) { + this.iname = name; + } + + public MarkerGroupItem(String name) { + this.name = name; + } + + public String getName(OsmandApplication app) { + if (name == null && iname != 0) { + name = app.getString(iname); + } + return name; } - return monthStr; } + public class CategoriesSubHeader { + + private MapMarkersGroup group; + + public CategoriesSubHeader(MapMarkersGroup group) { + this.group = group; + } + + public MapMarkersGroup getGroup() { + return group; + } + } + + public interface MapMarkersGroupsAdapterListener { void onItemClick(View view); diff --git a/OsmAnd/src/net/osmand/plus/measurementtool/GraphsCard.java b/OsmAnd/src/net/osmand/plus/measurementtool/GraphsCard.java index 341b373721..0d4a5b7389 100644 --- a/OsmAnd/src/net/osmand/plus/measurementtool/GraphsCard.java +++ b/OsmAnd/src/net/osmand/plus/measurementtool/GraphsCard.java @@ -24,6 +24,7 @@ import net.osmand.plus.GpxSelectionHelper.GpxDisplayItem; import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; import net.osmand.plus.activities.MapActivity; +import net.osmand.plus.helpers.AndroidUiHelper; import net.osmand.plus.helpers.GpxUiHelper; import net.osmand.plus.helpers.GpxUiHelper.LineGraphType; import net.osmand.plus.mapcontextmenu.other.HorizontalSelectionAdapter; @@ -109,9 +110,16 @@ public class GraphsCard extends BaseCard implements OnUpdateInfoListener { GraphAdapterHelper.bindGraphAdapters(commonGraphAdapter, Collections.singletonList((BaseGraphAdapter) customGraphAdapter), (ViewGroup) view); refreshMapCallback = GraphAdapterHelper.bindToMap(commonGraphAdapter, mapActivity, trackDetailsMenu); + updateTopPadding(); fullUpdate(); } + private void updateTopPadding() { + int topPadding = AndroidUiHelper.isOrientationPortrait(mapActivity) ? + 0 : app.getResources().getDimensionPixelSize(R.dimen.content_padding_small); + view.setPadding(0, topPadding, 0, 0); + } + @Override public int getCardLayoutId() { return R.layout.measurement_tool_graph_card; diff --git a/OsmAnd/src/net/osmand/plus/measurementtool/MeasurementToolFragment.java b/OsmAnd/src/net/osmand/plus/measurementtool/MeasurementToolFragment.java index 49815c05dd..57d2cd36c6 100644 --- a/OsmAnd/src/net/osmand/plus/measurementtool/MeasurementToolFragment.java +++ b/OsmAnd/src/net/osmand/plus/measurementtool/MeasurementToolFragment.java @@ -11,6 +11,7 @@ import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; +import android.view.ViewGroup.MarginLayoutParams; import android.widget.FrameLayout; import android.widget.ImageButton; import android.widget.ImageView; @@ -82,9 +83,13 @@ import net.osmand.plus.views.layers.MapControlsLayer.MapControlsThemeInfoProvide import net.osmand.plus.views.mapwidgets.MapInfoWidgetsFactory.TopToolbarController; import net.osmand.plus.views.mapwidgets.MapInfoWidgetsFactory.TopToolbarControllerType; import net.osmand.plus.views.mapwidgets.MapInfoWidgetsFactory.TopToolbarView; -import net.osmand.plus.widgets.MultiStateToggleButton; -import net.osmand.plus.widgets.MultiStateToggleButton.OnRadioItemClickListener; -import net.osmand.plus.widgets.MultiStateToggleButton.RadioItem; +import net.osmand.plus.widgets.multistatetoggle.IconToggleButton; +import net.osmand.plus.widgets.multistatetoggle.IconToggleButton.IconRadioItem; +import net.osmand.plus.widgets.multistatetoggle.MultiStateToggleButton; +import net.osmand.plus.widgets.multistatetoggle.RadioItem; +import net.osmand.plus.widgets.multistatetoggle.RadioItem.OnRadioItemClickListener; +import net.osmand.plus.widgets.multistatetoggle.TextToggleButton; +import net.osmand.plus.widgets.multistatetoggle.TextToggleButton.TextRadioItem; import net.osmand.router.RoutePlannerFrontEnd.GpxRouteApproximation; import net.osmand.util.Algorithms; @@ -130,6 +135,7 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route private RadioItem graphBtn; private View mainView; private View bottomMapControls; + private View topMapControls; private ImageView upDownBtn; private ImageView undoBtn; private ImageView redoBtn; @@ -251,7 +257,7 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route public void showProgressBar() { MeasurementToolFragment.this.showProgressBar(); updateInfoView(); - updateCardContainerSize(); + updateInfoViewAppearance(); } @Override @@ -261,10 +267,10 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route @Override public void hideProgressBar() { - ((ProgressBar) mainView.findViewById(R.id.snap_to_road_progress_bar)).setVisibility(View.GONE); + ((ProgressBar) mainView.findViewById(R.id.snap_to_road_progress_bar)).setVisibility(View.INVISIBLE); progressBarVisible = false; updateInfoView(); - updateCardContainerSize(); + updateInfoViewAppearance(); } @Override @@ -287,32 +293,38 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route .inflate(R.layout.fragment_measurement_tool, container, false); mainView = view.findViewById(R.id.main_view); - AndroidUtils.setBackground(mapActivity, mainView, nightMode, R.drawable.bg_bottom_menu_light, R.drawable.bg_bottom_menu_dark); detailsMenu = new GraphDetailsMenu(); + LinearLayout infoButtonsContainer = mainView.findViewById(R.id.custom_radio_buttons); if (portrait) { cardsContainer = mainView.findViewById(R.id.cards_container); + infoTypeBtn = new TextToggleButton(app, infoButtonsContainer, nightMode); String pointsBtnTitle = getString(R.string.shared_string_gpx_points); - pointsBtn = new RadioItem(pointsBtnTitle); - pointsBtn.setOnClickListener(getInfoTypeBtnListener(InfoType.POINTS)); + pointsBtn = new TextRadioItem(pointsBtnTitle); String graphBtnTitle = getString(R.string.shared_string_graph); - graphBtn = new RadioItem(graphBtnTitle); - graphBtn.setOnClickListener(getInfoTypeBtnListener(InfoType.GRAPH)); + graphBtn = new TextRadioItem(graphBtnTitle); - LinearLayout infoButtonsContainer = mainView.findViewById(R.id.custom_radio_buttons); - infoTypeBtn = new MultiStateToggleButton(app, infoButtonsContainer, nightMode); - infoTypeBtn.setItems(pointsBtn, graphBtn); } else { cardsContainer = mapActivity.findViewById(R.id.left_side_menu); bottomMapControls = mapActivity.findViewById(R.id.bottom_controls_container); + topMapControls = mapActivity.findViewById(R.id.top_controls_container); + + infoTypeBtn = new IconToggleButton(app, infoButtonsContainer, nightMode); + pointsBtn = new IconRadioItem(R.drawable.ic_action_plan_route_point_colored, true); + graphBtn = new IconRadioItem(R.drawable.ic_action_analyze_intervals); + ScrollUtils.addOnGlobalLayoutListener(mainView, new Runnable() { @Override public void run() { - updateCardContainerSize(); + updateInfoViewAppearance(); } }); } + pointsBtn.setOnClickListener(getInfoTypeBtnListener(InfoType.POINTS)); + graphBtn.setOnClickListener(getInfoTypeBtnListener(InfoType.GRAPH)); + infoTypeBtn.setItems(pointsBtn, graphBtn); + pointsCard = new PointsCard(mapActivity, this); graphsCard = new GraphsCard(mapActivity, detailsMenu, this); @@ -348,7 +360,7 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route public void onClick(View v) { if (infoExpanded) { collapseInfoView(); - } else if (setInfoType(InfoType.POINTS) && portrait) { + } else if (setInfoType(InfoType.POINTS)) { infoTypeBtn.setSelectedItem(pointsBtn); } } @@ -626,7 +638,7 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route if (portrait) { setMapPosition(OsmandSettings.MIDDLE_TOP_CONSTANT); } else { - shiftBottomMapControls(false); + shiftMapControls(false); setMapPosition(OsmandSettings.LANDSCAPE_MIDDLE_RIGHT_CONSTANT); } cardsContainer.setVisibility(View.VISIBLE); @@ -640,15 +652,14 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route } private void collapseInfoView() { - cardsContainer.setVisibility(View.GONE); - if (portrait) { - infoTypeBtn.setSelectedItem(null); - } else { - shiftBottomMapControls(true); - } infoExpanded = false; currentInfoType = null; - setDefaultMapPosition(); + cardsContainer.setVisibility(View.GONE); + if (!portrait) { + shiftMapControls(true); + } + infoTypeBtn.setSelectedItem(null); + moveMapToDefaultPosition(); updateUpDownBtn(); } @@ -676,37 +687,37 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route } } - private void updateCardContainerSize() { - if (portrait) { - return; + private void updateInfoViewAppearance() { + if (portrait) return; + + View toolsPanel = mainView.findViewById(R.id.measure_mode_controls); + View snapToRoadProgress = mainView.findViewById(R.id.snap_to_road_progress_bar); + + int infoViewWidth = mainView.getWidth() - toolsPanel.getWidth(); + int bottomMargin = toolsPanel.getHeight(); + if (progressBarVisible) { + bottomMargin += snapToRoadProgress.getHeight(); } - View measureModeControls = mainView.findViewById(R.id.measure_mode_controls); - int width = mainView.getWidth() - measureModeControls.getWidth(); - int bottomMargin = measureModeControls.getHeight(); - bottomMargin = progressBarVisible ? bottomMargin + mainView.findViewById(R.id.snap_to_road_progress_bar).getHeight() : bottomMargin; + + ViewGroup.MarginLayoutParams params = null; if (mainView.getParent() instanceof FrameLayout) { - FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(width, -1); - params.setMargins(0, 0, 0, bottomMargin); - cardsContainer.setLayoutParams(params); + params = new FrameLayout.LayoutParams(infoViewWidth, -1); } else if (mainView.getParent() instanceof LinearLayout) { - LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(width, -1); - params.setMargins(0, 0, 0, bottomMargin); + params = new LinearLayout.LayoutParams(infoViewWidth, -1); + } + if (params != null) { + AndroidUtils.setMargins(params, 0, 0, 0, bottomMargin); cardsContainer.setLayoutParams(params); } } - private void shiftBottomMapControls(boolean toInitialPosition) { - if (portrait) { - return; - } - int leftMargin = toInitialPosition ? 0 : cardsContainer.getWidth(); - if (bottomMapControls.getParent() instanceof LinearLayout) { - LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) bottomMapControls.getLayoutParams(); - params.setMargins(leftMargin, 0, 0, 0); - } else if (bottomMapControls.getParent() instanceof FrameLayout) { - FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) bottomMapControls.getLayoutParams(); - params.setMargins(leftMargin, 0, 0, 0); - } + private void shiftMapControls(boolean toInitialPosition) { + boolean isLayoutRtl = AndroidUtils.isLayoutRtl(getContext()); + int shiftPosition = toInitialPosition ? 0 : cardsContainer.getWidth(); + int start = isLayoutRtl ? 0 : shiftPosition; + int end = isLayoutRtl ? shiftPosition : 0; + AndroidUtils.setMargins((MarginLayoutParams) bottomMapControls.getLayoutParams(), start, 0, end, 0); + AndroidUtils.setMargins((MarginLayoutParams) topMapControls.getLayoutParams(), start, 0, end, 0); } public boolean isInEditMode() { @@ -772,7 +783,7 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route mapActivity.getMapLayers().getMapControlsLayer().addThemeInfoProviderTag(TAG); mapActivity.getMapLayers().getMapControlsLayer().showMapControlsIfHidden(); cachedMapPosition = mapActivity.getMapView().getMapPosition(); - setDefaultMapPosition(); + moveMapToDefaultPosition(); addInitialPoint(); } } @@ -1196,7 +1207,7 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route @Override public void onCloseMenu() { - setDefaultMapPosition(); + moveMapToDefaultPosition(); } @Override @@ -1390,7 +1401,7 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route editingCtx.cancelSnapToRoad(); MapActivity mapActivity = getMapActivity(); if (mapActivity != null) { - mainView.findViewById(R.id.snap_to_road_progress_bar).setVisibility(View.GONE); + mainView.findViewById(R.id.snap_to_road_progress_bar).setVisibility(View.INVISIBLE); mapActivity.refreshMap(); } } @@ -1641,7 +1652,7 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route updateInfoView(); } - private void setDefaultMapPosition() { + private void moveMapToDefaultPosition() { setMapPosition(OsmandSettings.CENTER_CONSTANT); } diff --git a/OsmAnd/src/net/osmand/plus/monitoring/TripRecordingBottomSheet.java b/OsmAnd/src/net/osmand/plus/monitoring/TripRecordingBottomSheet.java index 89734b7415..41cff45f1e 100644 --- a/OsmAnd/src/net/osmand/plus/monitoring/TripRecordingBottomSheet.java +++ b/OsmAnd/src/net/osmand/plus/monitoring/TripRecordingBottomSheet.java @@ -83,7 +83,7 @@ public class TripRecordingBottomSheet extends MenuBottomSheetDialogFragment impl public static final String UPDATE_DYNAMIC_ITEMS = "update_dynamic_items"; private static final int GPS_UPDATE_INTERVAL = 1000; public static final GPXTabItemType[] INIT_TAB_ITEMS = - new GPXTabItemType[]{GPX_TAB_ITEM_GENERAL, GPX_TAB_ITEM_ALTITUDE, GPX_TAB_ITEM_SPEED}; + new GPXTabItemType[] {GPX_TAB_ITEM_GENERAL, GPX_TAB_ITEM_ALTITUDE, GPX_TAB_ITEM_SPEED}; private OsmandApplication app; private OsmandSettings settings; @@ -651,7 +651,7 @@ public class TripRecordingBottomSheet extends MenuBottomSheetDialogFragment impl } @Override - public void openAnalyzeOnMap(GpxDisplayItem gpxItem) { + public void openAnalyzeOnMap(@NonNull GpxDisplayItem gpxItem) { } public interface DismissTargetFragment { diff --git a/OsmAnd/src/net/osmand/plus/myplaces/DeletePointsTask.java b/OsmAnd/src/net/osmand/plus/myplaces/DeletePointsTask.java index c7179f1091..996d52cfec 100644 --- a/OsmAnd/src/net/osmand/plus/myplaces/DeletePointsTask.java +++ b/OsmAnd/src/net/osmand/plus/myplaces/DeletePointsTask.java @@ -8,7 +8,8 @@ import net.osmand.plus.GpxSelectionHelper.GpxDisplayItem; import net.osmand.plus.GpxSelectionHelper.GpxDisplayItemType; import net.osmand.plus.OsmandApplication; import net.osmand.plus.activities.SavingTrackHelper; -import net.osmand.plus.itinerary.ItineraryGroup; +import net.osmand.plus.mapmarkers.MapMarkersGroup; +import net.osmand.plus.mapmarkers.MapMarkersHelper; import java.io.File; import java.lang.ref.WeakReference; @@ -64,9 +65,10 @@ public class DeletePointsTask extends AsyncTask { } private void syncGpx(GPXFile gpxFile) { - ItineraryGroup group = app.getItineraryHelper().getMarkersGroup(gpxFile); + MapMarkersHelper helper = app.getMapMarkersHelper(); + MapMarkersGroup group = helper.getMarkersGroup(gpxFile); if (group != null) { - app.getItineraryHelper().runSynchronization(group); + helper.runSynchronization(group); } } diff --git a/OsmAnd/src/net/osmand/plus/myplaces/EditTrackGroupDialogFragment.java b/OsmAnd/src/net/osmand/plus/myplaces/EditTrackGroupDialogFragment.java index 352e7ecc3b..674ee76042 100644 --- a/OsmAnd/src/net/osmand/plus/myplaces/EditTrackGroupDialogFragment.java +++ b/OsmAnd/src/net/osmand/plus/myplaces/EditTrackGroupDialogFragment.java @@ -49,7 +49,7 @@ import net.osmand.plus.base.bottomsheetmenu.SimpleBottomSheetItem; import net.osmand.plus.base.bottomsheetmenu.simpleitems.TitleItem; import net.osmand.plus.helpers.AndroidUiHelper; import net.osmand.plus.helpers.FontCache; -import net.osmand.plus.itinerary.ItineraryGroup; +import net.osmand.plus.mapmarkers.MapMarkersGroup; import net.osmand.plus.mapmarkers.MapMarkersHelper; import net.osmand.plus.measurementtool.OptionsDividerItem; import net.osmand.plus.myplaces.DeletePointsTask.OnPointsDeleteListener; @@ -192,7 +192,7 @@ public class EditTrackGroupDialogFragment extends MenuBottomSheetDialogFragment } private BaseBottomSheetItem createCopyToMarkersItem(final GPXFile gpxFile) { - ItineraryGroup markersGroup = app.getItineraryHelper().getMarkersGroup(gpxFile); + MapMarkersGroup markersGroup = mapMarkersHelper.getMarkersGroup(gpxFile); final boolean synced = markersGroup != null && (Algorithms.isEmpty(markersGroup.getWptCategories()) || markersGroup.getWptCategories().contains(group.getName())); @@ -216,10 +216,10 @@ public class EditTrackGroupDialogFragment extends MenuBottomSheetDialogFragment selectedGpxHelper.selectGpxFile(gpxFile, true, false, false, false, false); } boolean groupCreated = false; - ItineraryGroup markersGroup = app.getItineraryHelper().getMarkersGroup(gpxFile); + MapMarkersGroup markersGroup = mapMarkersHelper.getMarkersGroup(gpxFile); if (markersGroup == null) { groupCreated = true; - markersGroup = app.getItineraryHelper().addOrEnableGroup(gpxFile); + markersGroup = mapMarkersHelper.addOrEnableGroup(gpxFile); } Set categories = markersGroup.getWptCategories(); Set selectedCategories = new HashSet<>(); @@ -234,9 +234,9 @@ public class EditTrackGroupDialogFragment extends MenuBottomSheetDialogFragment if (Algorithms.isEmpty(selectedCategories)) { mapMarkersHelper.removeMarkersGroup(markersGroup); } else { - app.getItineraryHelper().updateGroupWptCategories(markersGroup, selectedCategories); + mapMarkersHelper.updateGroupWptCategories(markersGroup, selectedCategories); if (!groupCreated) { - app.getItineraryHelper().runSynchronization(markersGroup); + mapMarkersHelper.runSynchronization(markersGroup); } } } @@ -524,9 +524,10 @@ public class EditTrackGroupDialogFragment extends MenuBottomSheetDialogFragment } private void syncGpx(GPXFile gpxFile) { - ItineraryGroup group = app.getItineraryHelper().getMarkersGroup(gpxFile); + MapMarkersHelper markersHelper = app.getMapMarkersHelper(); + MapMarkersGroup group = markersHelper.getMarkersGroup(gpxFile); if (group != null) { - app.getItineraryHelper().runSynchronization(group); + markersHelper.runSynchronization(group); } } } diff --git a/OsmAnd/src/net/osmand/plus/myplaces/SegmentActionsListener.java b/OsmAnd/src/net/osmand/plus/myplaces/SegmentActionsListener.java index 3851e7e102..586b22634d 100644 --- a/OsmAnd/src/net/osmand/plus/myplaces/SegmentActionsListener.java +++ b/OsmAnd/src/net/osmand/plus/myplaces/SegmentActionsListener.java @@ -2,6 +2,8 @@ package net.osmand.plus.myplaces; import android.view.View; +import androidx.annotation.NonNull; + import net.osmand.GPXUtilities.TrkSegment; import net.osmand.plus.GpxSelectionHelper.GpxDisplayItem; @@ -19,5 +21,5 @@ public interface SegmentActionsListener { void showOptionsPopupMenu(View view, TrkSegment trkSegment, boolean confirmDeletion, GpxDisplayItem gpxItem); - void openAnalyzeOnMap(GpxDisplayItem gpxItem); + void openAnalyzeOnMap(@NonNull GpxDisplayItem gpxItem); } diff --git a/OsmAnd/src/net/osmand/plus/myplaces/SplitSegmentDialogFragment.java b/OsmAnd/src/net/osmand/plus/myplaces/SplitSegmentDialogFragment.java index c981610015..5ec7077718 100644 --- a/OsmAnd/src/net/osmand/plus/myplaces/SplitSegmentDialogFragment.java +++ b/OsmAnd/src/net/osmand/plus/myplaces/SplitSegmentDialogFragment.java @@ -287,6 +287,8 @@ public class SplitSegmentDialogFragment extends DialogFragment { } int color = app.getResources().getColor(colorId); title.setTextColor(color); + String titleText = getString(R.string.gpx_split_interval); + title.setText(getString(R.string.ltr_or_rtl_combine_via_colon, titleText, "")); text.setTextColor(color); img.setImageDrawable(ic.getIcon(R.drawable.ic_action_arrow_drop_down, colorId)); } diff --git a/OsmAnd/src/net/osmand/plus/myplaces/TrackPointFragment.java b/OsmAnd/src/net/osmand/plus/myplaces/TrackPointFragment.java index 2641fe986f..59be8cc533 100644 --- a/OsmAnd/src/net/osmand/plus/myplaces/TrackPointFragment.java +++ b/OsmAnd/src/net/osmand/plus/myplaces/TrackPointFragment.java @@ -63,7 +63,7 @@ import net.osmand.plus.base.OsmandExpandableListFragment; import net.osmand.plus.base.PointImageDrawable; import net.osmand.plus.helpers.GpxUiHelper; import net.osmand.plus.mapmarkers.CoordinateInputDialogFragment; -import net.osmand.plus.itinerary.ItineraryGroup; +import net.osmand.plus.mapmarkers.MapMarkersGroup; import net.osmand.plus.mapmarkers.MapMarkersHelper; import net.osmand.plus.myplaces.DeletePointsTask.OnPointsDeleteListener; import net.osmand.plus.myplaces.TrackBitmapDrawer.TrackBitmapDrawerListener; @@ -399,7 +399,8 @@ public class TrackPointFragment extends OsmandExpandableListFragment implements createMenuItem(menu, SHARE_ID, R.string.shared_string_share, R.drawable.ic_action_gshare_dark, MenuItem.SHOW_AS_ACTION_NEVER, true); GPXFile gpxFile = getGpx(); if (gpxFile != null && gpxFile.path != null) { - final boolean synced = app.getItineraryHelper().getMarkersGroup(getGpx()) != null; + final MapMarkersHelper markersHelper = app.getMapMarkersHelper(); + final boolean synced = markersHelper.getMarkersGroup(getGpx()) != null; createMenuItem(menu, SELECT_MAP_MARKERS_ID, synced ? R.string.remove_from_map_markers : R.string.shared_string_add_to_map_markers, R.drawable.ic_action_flag, MenuItem.SHOW_AS_ACTION_NEVER); } @@ -500,15 +501,15 @@ public class TrackPointFragment extends OsmandExpandableListFragment implements return; } final GPXFile gpxFile = getGpx(); - ItineraryGroup markersSearch = app.getItineraryHelper().getMarkersGroup(gpxFile); - final ItineraryGroup markersGr; + MapMarkersGroup markersSearch = markersHelper.getMarkersGroup(gpxFile); + final MapMarkersGroup markersGr; final boolean markersRemoved; if (markersSearch != null) { markersGr = markersSearch; markersHelper.removeMarkersGroup(markersGr); markersRemoved = true; } else if (gpxFile != null) { - markersGr = app.getItineraryHelper().addOrEnableGroup(gpxFile); + markersGr = markersHelper.addOrEnableGroup(gpxFile); markersRemoved = false; } else { markersRemoved = false; @@ -534,10 +535,10 @@ public class TrackPointFragment extends OsmandExpandableListFragment implements if (trackActivity != null) { if (markersRemoved) { if (gpxFile != null) { - app.getItineraryHelper().addOrEnableGroup(gpxFile); + markersHelper.addOrEnableGroup(gpxFile); } } else { - ItineraryGroup group = app.getItineraryHelper().getMarkersGroup(gpxFile); + MapMarkersGroup group = markersHelper.getMarkersGroup(gpxFile); if (group != null) { markersHelper.removeMarkersGroup(group); } diff --git a/OsmAnd/src/net/osmand/plus/myplaces/TrackSegmentFragment.java b/OsmAnd/src/net/osmand/plus/myplaces/TrackSegmentFragment.java index c95d511526..9352cfc0a2 100644 --- a/OsmAnd/src/net/osmand/plus/myplaces/TrackSegmentFragment.java +++ b/OsmAnd/src/net/osmand/plus/myplaces/TrackSegmentFragment.java @@ -292,7 +292,7 @@ public class TrackSegmentFragment extends OsmAndListFragment implements TrackBit } @Override - public void openAnalyzeOnMap(GpxDisplayItem gpxItem) { + public void openAnalyzeOnMap(@NonNull GpxDisplayItem gpxItem) { OsmandSettings settings = app.getSettings(); settings.setMapLocationToShow(gpxItem.locationOnMap.lat, gpxItem.locationOnMap.lon, settings.getLastKnownMapZoom(), diff --git a/OsmAnd/src/net/osmand/plus/resources/IncrementalChangesManager.java b/OsmAnd/src/net/osmand/plus/resources/IncrementalChangesManager.java index 2f5ba258f0..565d300e47 100644 --- a/OsmAnd/src/net/osmand/plus/resources/IncrementalChangesManager.java +++ b/OsmAnd/src/net/osmand/plus/resources/IncrementalChangesManager.java @@ -1,12 +1,11 @@ package net.osmand.plus.resources; -import android.view.LayoutInflater; - import net.osmand.IndexConstants; import net.osmand.PlatformUtil; import net.osmand.binary.BinaryMapIndexReader; import net.osmand.osm.io.NetworkUtils; import net.osmand.plus.R; +import net.osmand.plus.download.SrtmDownloadItem; import net.osmand.util.Algorithms; import org.apache.commons.logging.Log; @@ -33,12 +32,11 @@ public class IncrementalChangesManager { private static final org.apache.commons.logging.Log log = PlatformUtil.getLog(IncrementalChangesManager.class); private ResourceManager resourceManager; private final Map regions = new ConcurrentHashMap(); - - + public IncrementalChangesManager(ResourceManager resourceManager) { this.resourceManager = resourceManager; } - + public List collectChangesFiles(File dir, String ext, List files) { if (dir.exists() && dir.canRead()) { File[] lf = dir.listFiles(); @@ -47,8 +45,8 @@ public class IncrementalChangesManager { } Set existingFiles = new HashSet(); for (File f : files) { - if(!f.getName().endsWith(IndexConstants.BINARY_WIKI_MAP_INDEX_EXT) && - !f.getName().endsWith(IndexConstants.BINARY_SRTM_MAP_INDEX_EXT)) { + if (!f.getName().endsWith(IndexConstants.BINARY_WIKI_MAP_INDEX_EXT) && + !SrtmDownloadItem.isSrtmFile(f.getName())) { existingFiles.add(Algorithms.getFileNameWithoutExtension(f)); } } @@ -71,7 +69,7 @@ public class IncrementalChangesManager { public synchronized void indexMainMap(File f, long dateCreated) { String nm = Algorithms.getFileNameWithoutExtension(f).toLowerCase(); RegionUpdateFiles regionUpdateFiles = regions.get(nm); - if(regionUpdateFiles == null) { + if (regionUpdateFiles == null) { regionUpdateFiles = new RegionUpdateFiles(nm); regions.put(nm, regionUpdateFiles); } @@ -98,7 +96,7 @@ public class IncrementalChangesManager { RegionUpdate monthRu = regionUpdateFiles.monthUpdates.get(month); while (it.hasNext()) { RegionUpdate ru = it.next(); - if(ru == null) { + if (ru == null) { continue; } if (ru.obfCreated <= dateCreated || @@ -114,58 +112,58 @@ public class IncrementalChangesManager { } } } - + public synchronized boolean index(File f, long dateCreated, BinaryMapIndexReader mapReader) { String index = Algorithms.getFileNameWithoutExtension(f).toLowerCase(); - if(index.length() <= 9 || index.charAt(index.length() - 9) != '_'){ + if (index.length() <= 9 || index.charAt(index.length() - 9) != '_') { return false; } String nm = index.substring(0, index.length() - 9); String date = index.substring(index.length() - 9 + 1); RegionUpdateFiles regionUpdateFiles = regions.get(nm); - if(regionUpdateFiles == null) { + if (regionUpdateFiles == null) { regionUpdateFiles = new RegionUpdateFiles(nm); regions.put(nm, regionUpdateFiles); } return regionUpdateFiles.addUpdate(date, f, dateCreated); } - + protected static String formatSize(long vl) { return (vl * 1000 / (1 << 20l)) / 1000.0f + ""; } - + public static long calculateSize(List list) { long l = 0; - for(IncrementalUpdate iu : list) { + for (IncrementalUpdate iu : list) { l += iu.containerSize; } return l; - } - + } + protected class RegionUpdate { protected File file; protected String date; - protected long obfCreated; + protected long obfCreated; } - + protected class RegionUpdateFiles { protected String nm; protected File mainFile; protected long mainFileInit; TreeMap> dayUpdates = new TreeMap>(); TreeMap monthUpdates = new TreeMap(); - + public RegionUpdateFiles(String nm) { this.nm = nm; } - + public boolean addUpdate(String date, File file, long dateCreated) { String monthYear = date.substring(0, 5); RegionUpdate ru = new RegionUpdate(); ru.date = date; ru.file = file; ru.obfCreated = dateCreated; - if(date.endsWith("00")) { + if (date.endsWith("00")) { monthUpdates.put(monthYear, ru); } else { List list = dayUpdates.get(monthYear); @@ -179,22 +177,21 @@ public class IncrementalChangesManager { } } - + public class IncrementalUpdateList { - public TreeMap updateByMonth = + public TreeMap updateByMonth = new TreeMap(); public String errorMessage; public RegionUpdateFiles updateFiles; - - + public boolean isPreferrableLimitForDayUpdates(String monthYearPart, List dayUpdates) { List lst = updateFiles.dayUpdates.get(monthYearPart); - if(lst == null || lst.size() < 10) { + if (lst == null || lst.size() < 10) { return true; } return false; } - + public List getItemsForUpdate() { Iterator it = updateByMonth.values().iterator(); List ll = new ArrayList(); @@ -208,7 +205,7 @@ public class IncrementalChangesManager { } else { // it causes problem when person doesn't restart application for 10 days so updates stop working // && isPreferrableLimitForDayUpdates(n.monthYearPart, n.getDayUpdates()) - if (n.isDayUpdateApplicable() ) { + if (n.isDayUpdateApplicable()) { ll.addAll(n.getDayUpdates()); } else if (n.isMonthUpdateApplicable()) { ll.addAll(n.getMonthUpdate()); @@ -234,51 +231,51 @@ public class IncrementalChangesManager { } } } - + protected static class IncrementalUpdateGroupByMonth { - public final String monthYearPart ; + public final String monthYearPart; public List dayUpdates = new ArrayList(); public IncrementalUpdate monthUpdate; - + public long calculateSizeMonthUpdates() { return calculateSize(getMonthUpdate()); } - + public long calculateSizeDayUpdates() { return calculateSize(getDayUpdates()); } - + public boolean isMonthUpdateApplicable() { return monthUpdate != null; } - + public boolean isDayUpdateApplicable() { boolean inLimits = dayUpdates.size() > 0 && dayUpdates.size() < 4; - if(!inLimits) { + if (!inLimits) { return false; } return true; } - + public List getMonthUpdate() { List ll = new ArrayList(); - if(monthUpdate == null) { + if (monthUpdate == null) { return ll; } ll.add(monthUpdate); - for(IncrementalUpdate iu : dayUpdates) { - if(iu.timestamp > monthUpdate.timestamp) { + for (IncrementalUpdate iu : dayUpdates) { + if (iu.timestamp > monthUpdate.timestamp) { ll.add(iu); } } return ll; } - + public List getDayUpdates() { return dayUpdates; } - - public IncrementalUpdateGroupByMonth(String monthYearPart ) { + + public IncrementalUpdateGroupByMonth(String monthYearPart) { this.monthYearPart = monthYearPart; } } @@ -300,7 +297,7 @@ public class IncrementalChangesManager { return "Update " + fileName + " " + sizeText + " MB " + date + ", timestamp: " + timestamp; } } - + private List getIncrementalUpdates(String file, long timestamp) throws IOException, XmlPullParserException { String url = URL + "?aosmc=true×tamp=" + timestamp + "&file=" + URLEncoder.encode(file); @@ -314,7 +311,7 @@ public class IncrementalChangesManager { int elements = 0; while (parser.next() != XmlPullParser.END_DOCUMENT) { if (parser.getEventType() == XmlPullParser.START_TAG) { - elements ++; + elements++; if (parser.getName().equals("update")) { IncrementalUpdate dt = new IncrementalUpdate(); dt.date = parser.getAttributeValue("", "updateDate"); @@ -332,21 +329,19 @@ public class IncrementalChangesManager { conn.disconnect(); return lst; } - - public IncrementalUpdateList getUpdatesByMonth(String fileName) { IncrementalUpdateList iul = new IncrementalUpdateList(); RegionUpdateFiles ruf = regions.get(fileName.toLowerCase()); iul.updateFiles = ruf; - if(ruf == null) { + if (ruf == null) { iul.errorMessage = resourceManager.getContext().getString(R.string.no_updates_available); return iul; } long timestamp = getTimestamp(ruf); try { List lst = getIncrementalUpdates(fileName, timestamp); - for(IncrementalUpdate iu : lst) { + for (IncrementalUpdate iu : lst) { iul.addUpdate(iu); } } catch (Exception e) { @@ -357,9 +352,9 @@ public class IncrementalChangesManager { return iul; } - public long getUpdatesSize(String fileName){ + public long getUpdatesSize(String fileName) { RegionUpdateFiles ruf = regions.get(fileName.toLowerCase()); - if(ruf == null) { + if (ruf == null) { return 0; } long size = 0; @@ -374,9 +369,9 @@ public class IncrementalChangesManager { return size; } - public void deleteUpdates(String fileName){ + public void deleteUpdates(String fileName) { RegionUpdateFiles ruf = regions.get(fileName.toLowerCase()); - if(ruf == null) { + if (ruf == null) { return; } for (List regionUpdates : ruf.dayUpdates.values()) { @@ -399,7 +394,7 @@ public class IncrementalChangesManager { public long getTimestamp(String fileName) { RegionUpdateFiles ruf = regions.get(fileName.toLowerCase()); - if(ruf == null) { + if (ruf == null) { return System.currentTimeMillis(); } return getTimestamp(ruf); @@ -407,7 +402,7 @@ public class IncrementalChangesManager { public long getMapTimestamp(String fileName) { RegionUpdateFiles ruf = regions.get(fileName.toLowerCase()); - if(ruf == null) { + if (ruf == null) { return System.currentTimeMillis(); } return ruf.mainFileInit; diff --git a/OsmAnd/src/net/osmand/plus/resources/ResourceManager.java b/OsmAnd/src/net/osmand/plus/resources/ResourceManager.java index e48e37878a..7263eab1d8 100644 --- a/OsmAnd/src/net/osmand/plus/resources/ResourceManager.java +++ b/OsmAnd/src/net/osmand/plus/resources/ResourceManager.java @@ -41,6 +41,7 @@ import net.osmand.plus.R; import net.osmand.plus.Version; import net.osmand.plus.download.DownloadOsmandIndexesHelper; import net.osmand.plus.download.DownloadOsmandIndexesHelper.AssetEntry; +import net.osmand.plus.download.SrtmDownloadItem; import net.osmand.plus.inapp.InAppPurchaseHelper; import net.osmand.plus.render.MapRenderRepositories; import net.osmand.plus.render.NativeOsmandLibrary; @@ -78,11 +79,11 @@ import java.util.concurrent.ConcurrentHashMap; import static net.osmand.IndexConstants.VOICE_INDEX_DIR; /** - * Resource manager is responsible to work with all resources + * Resource manager is responsible to work with all resources * that could consume memory (especially with file resources). * Such as indexes, tiles. * Also it is responsible to create cache for that resources if they - * can't be loaded fully into memory & clear them on request. + * can't be loaded fully into memory & clear them on request. */ public class ResourceManager { @@ -91,10 +92,10 @@ public class ResourceManager { public static final String DEFAULT_WIKIVOYAGE_TRAVEL_OBF = "Default_wikivoyage.travel.obf"; private static final Log log = PlatformUtil.getLog(ResourceManager.class); - + protected static ResourceManager manager = null; - protected File dirWithTiles ; + protected File dirWithTiles; private List tilesCacheList = new ArrayList<>(); private BitmapTilesCache bitmapTilesCache; @@ -115,21 +116,22 @@ public class ResourceManager { STREET_LOOKUP, TRANSPORT, ADDRESS, - QUICK_SEARCH, + QUICK_SEARCH, ROUTING, TRANSPORT_ROUTING } - + public static class BinaryMapReaderResource { private BinaryMapIndexReader initialReader; private File filename; private List readers = new ArrayList<>(BinaryMapReaderResourceType.values().length); private boolean useForRouting; private boolean useForPublicTransport; + public BinaryMapReaderResource(File f, BinaryMapIndexReader initialReader) { this.filename = f; this.initialReader = initialReader; - while(readers.size() < BinaryMapReaderResourceType.values().length) { + while (readers.size() < BinaryMapReaderResourceType.values().length) { readers.add(null); } } @@ -173,7 +175,7 @@ public class ResourceManager { } initialReader = null; } - + public boolean isClosed() { return initialReader == null; } @@ -189,7 +191,7 @@ public class ResourceManager { public void setUseForRouting(boolean useForRouting) { this.useForRouting = useForRouting; } - + public boolean isUseForRouting() { return useForRouting; } @@ -202,34 +204,32 @@ public class ResourceManager { this.useForPublicTransport = useForPublicTransport; } } - + protected final Map fileReaders = new ConcurrentHashMap(); - - + private final Map addressMap = new ConcurrentHashMap(); - protected final Map amenityRepositories = new ConcurrentHashMap(); -// protected final Map routingMapFiles = new ConcurrentHashMap(); + protected final Map amenityRepositories = new ConcurrentHashMap(); + // protected final Map routingMapFiles = new ConcurrentHashMap(); protected final Map transportRepositories = new ConcurrentHashMap(); protected final Map travelRepositories = new ConcurrentHashMap(); protected final Map indexFileNames = new ConcurrentHashMap(); protected final Map basemapFileNames = new ConcurrentHashMap(); - - + protected final IncrementalChangesManager changesManager = new IncrementalChangesManager(this); - + protected final MapRenderRepositories renderer; protected final MapTileDownloader tileDownloader; - + public final AsyncLoadingThread asyncLoadingThread = new AsyncLoadingThread(this); - + private HandlerThread renderingBufferImageThread; - + protected boolean internetIsNotAccessible = false; private boolean depthContours; - + public ResourceManager(OsmandApplication context) { - + this.context = context; this.renderer = new MapRenderRepositories(context); @@ -271,7 +271,7 @@ public class ResourceManager { public MapTileDownloader getMapTileDownloader() { return tileDownloader; } - + public HandlerThread getRenderingBufferImageThread() { return renderingBufferImageThread; } @@ -293,17 +293,17 @@ public class ResourceManager { // ".nomedia" indicates there are no pictures and no music to list in this dir for the Gallery app try { context.getAppPath(".nomedia").createNewFile(); //$NON-NLS-1$ - } catch( Exception e ) { + } catch (Exception e) { } for (TilesCache tilesCache : tilesCacheList) { tilesCache.setDirWithTiles(dirWithTiles); } } - + public java.text.DateFormat getDateFormat() { return DateFormat.getDateFormat(context); } - + public OsmandApplication getContext() { return context; } @@ -323,7 +323,7 @@ public class ResourceManager { return null; } - public synchronized void tileDownloaded(DownloadRequest request){ + public synchronized void tileDownloaded(DownloadRequest request) { if (request instanceof TileLoadDownloadRequest) { TileLoadDownloadRequest req = ((TileLoadDownloadRequest) request); TilesCache cache = getTilesCache(req.tileSource); @@ -332,13 +332,13 @@ public class ResourceManager { } } } - + public synchronized boolean tileExistOnFileSystem(String file, ITileSource map, int x, int y, int zoom) { TilesCache cache = getTilesCache(map); return cache != null && cache.tileExistOnFileSystem(file, map, x, y, zoom); } - - public void clearTileForMap(String file, ITileSource map, int x, int y, int zoom){ + + public void clearTileForMap(String file, ITileSource map, int x, int y, int zoom) { TilesCache cache = getTilesCache(map); if (cache != null) { cache.getTileForMap(file, map, x, y, zoom, true, false, true, true); @@ -376,7 +376,7 @@ public class ResourceManager { ////////////////////////////////////////////// Working with indexes //////////////////////////////////////////////// - public List reloadIndexesOnStart(AppInitializer progress, List warnings){ + public List reloadIndexesOnStart(AppInitializer progress, List warnings) { close(); // check we have some assets to copy to sdcard warnings.addAll(checkAssets(progress, false)); @@ -429,7 +429,7 @@ public class ResourceManager { return warnings; } - public List indexFontFiles(IProgress progress){ + public List indexFontFiles(IProgress progress) { File file = context.getAppPath(IndexConstants.FONT_INDEX_DIR); file.mkdirs(); List warnings = new ArrayList(); @@ -473,10 +473,10 @@ public class ResourceManager { log.error("Error while loading tts files from assets", e); } } - + public List checkAssets(IProgress progress, boolean forceUpdate) { String fv = Version.getFullVersion(context); - if(context.getAppInitializer().isAppVersionChanged()) { + if (context.getAppInitializer().isAppVersionChanged()) { copyMissingJSAssets(); } if (!fv.equalsIgnoreCase(context.getSettings().PREVIOUS_INSTALLED_VERSION.get()) || forceUpdate) { @@ -509,7 +509,7 @@ public class ResourceManager { } return Collections.emptyList(); } - + private void copyRegionsBoundaries() { try { File file = context.getAppPath("regions.ocbf"); @@ -522,7 +522,7 @@ public class ResourceManager { log.error(e.getMessage(), e); } } - + private void copyPoiTypes() { try { File file = context.getAppPath(IndexConstants.SETTINGS_DIR + "poi_types.xml"); @@ -540,6 +540,7 @@ public class ResourceManager { private final static String ASSET_COPY_MODE__overwriteOnlyIfExists = "overwriteOnlyIfExists"; private final static String ASSET_COPY_MODE__alwaysOverwriteOrCopy = "alwaysOverwriteOrCopy"; private final static String ASSET_COPY_MODE__copyOnlyIfDoesNotExist = "copyOnlyIfDoesNotExist"; + private void unpackBundledAssets(AssetManager assetManager, File appDataDir, IProgress progress, boolean isFirstInstall) throws IOException, XmlPullParserException { List assetEntries = DownloadOsmandIndexesHelper.getBundledAssets(assetManager); for (AssetEntry asset : assetEntries) { @@ -581,7 +582,7 @@ public class ResourceManager { } public static void copyAssets(AssetManager assetManager, String assetName, File file) throws IOException { - if(file.exists()){ + if (file.exists()) { Algorithms.removeAllFiles(file); } file.getParentFile().mkdirs(); @@ -593,9 +594,9 @@ public class ResourceManager { } private List collectFiles(File dir, String ext, List files) { - if(dir.exists() && dir.canRead()) { + if (dir.exists() && dir.canRead()) { File[] lf = dir.listFiles(); - if(lf == null || lf.length == 0) { + if (lf == null || lf.length == 0) { return files; } for (File f : lf) { @@ -606,12 +607,10 @@ public class ResourceManager { } return files; } - - - + private void renameRoadsFiles(ArrayList files, File roadsPath) { Iterator it = files.iterator(); - while(it.hasNext()) { + while (it.hasNext()) { File f = it.next(); if (f.getName().endsWith("-roads" + IndexConstants.BINARY_MAP_INDEX_EXT)) { f.renameTo(new File(roadsPath, f.getName().replace("-roads" + IndexConstants.BINARY_MAP_INDEX_EXT, @@ -632,7 +631,7 @@ public class ResourceManager { File appPath = context.getAppPath(null); File roadsPath = context.getAppPath(IndexConstants.ROADS_INDEX_DIR); roadsPath.mkdirs(); - + collectFiles(appPath, IndexConstants.BINARY_MAP_INDEX_EXT, files); renameRoadsFiles(files, roadsPath); collectFiles(roadsPath, IndexConstants.BINARY_MAP_INDEX_EXT, files); @@ -645,7 +644,7 @@ public class ResourceManager { if (OsmandPlugin.getEnabledPlugin(SRTMPlugin.class) != null || InAppPurchaseHelper.isContourLinesPurchased(context)) { collectFiles(context.getAppPath(IndexConstants.SRTM_INDEX_DIR), IndexConstants.BINARY_MAP_INDEX_EXT, files); } - + changesManager.collectChangesFiles(context.getAppPath(IndexConstants.LIVE_INDEX_DIR), IndexConstants.BINARY_MAP_INDEX_EXT, files); Collections.sort(files, Algorithms.getFileVersionComparator()); @@ -703,7 +702,7 @@ public class ResourceManager { log.error(String.format("File %s could not be read", f.getName()), e); } boolean wikiMap = (f.getName().contains("_wiki") || f.getName().contains(IndexConstants.BINARY_WIKI_MAP_INDEX_EXT)); - boolean srtmMap = f.getName().contains(IndexConstants.BINARY_SRTM_MAP_INDEX_EXT); + boolean srtmMap = SrtmDownloadItem.containsSrtmExtension(f.getName()); if (mapReader == null || (!Version.isPaidVersion(context) && wikiMap && !f.getName().equals(DEFAULT_WIKIVOYAGE_TRAVEL_OBF))) { warnings.add(MessageFormat.format(context.getString(R.string.version_index_is_not_supported), f.getName())); //$NON-NLS-1$ } else { @@ -714,9 +713,9 @@ public class ResourceManager { if (dateCreated == 0) { dateCreated = f.lastModified(); } - if(f.getParentFile().getName().equals(liveDir.getName())) { + if (f.getParentFile().getName().equals(liveDir.getName())) { boolean toUse = changesManager.index(f, dateCreated, mapReader); - if(!toUse) { + if (!toUse) { try { mapReader.close(); } catch (IOException e) { @@ -724,7 +723,7 @@ public class ResourceManager { } continue; } - } else if(!wikiMap && !srtmMap) { + } else if (!wikiMap && !srtmMap) { changesManager.indexMainMap(f, dateCreated); } indexFileNames.put(f.getName(), dateFormat.format(dateCreated)); //$NON-NLS-1$ @@ -733,7 +732,7 @@ public class ResourceManager { } renderer.initializeNewResource(progress, f, mapReader); BinaryMapReaderResource resource = new BinaryMapReaderResource(f, mapReader); - if (collectTravelFiles(resource)){ + if (collectTravelFiles(resource)) { //travel files are indexed continue; } @@ -746,7 +745,7 @@ public class ResourceManager { transportRepositories.put(f.getName(), resource); } // disable osmc for routing temporarily due to some bugs - if (mapReader.containsRouteData() && (!f.getParentFile().equals(liveDir) || + if (mapReader.containsRouteData() && (!f.getParentFile().equals(liveDir) || context.getSettings().USE_OSM_LIVE_FOR_ROUTING.get())) { resource.setUseForRouting(true); } @@ -789,7 +788,7 @@ public class ResourceManager { } } Iterator>> it = toAddPoiTypes.entrySet().iterator(); - while(it.hasNext()) { + while (it.hasNext()) { Entry> next = it.next(); PoiCategory category = next.getKey(); category.addExtraPoiTypes(next.getValue()); @@ -839,7 +838,7 @@ public class ResourceManager { } private boolean collectTravelFiles(BinaryMapReaderResource resource) { - if (resource.getFileName().contains(IndexConstants.BINARY_TRAVEL_GUIDE_MAP_INDEX_EXT)){ + if (resource.getFileName().contains(IndexConstants.BINARY_TRAVEL_GUIDE_MAP_INDEX_EXT)) { travelRepositories.put(resource.getFileName(), resource); return true; } @@ -855,7 +854,7 @@ public class ResourceManager { } } } - + ////////////////////////////////////////////// Working with amenities //////////////////////////////////////////////// public List getAmenityRepositories() { @@ -872,7 +871,7 @@ public class ResourceManager { } public List searchAmenities(SearchPoiTypeFilter filter, - double topLatitude, double leftLongitude, double bottomLatitude, double rightLongitude, int zoom, final ResultMatcher matcher) { + double topLatitude, double leftLongitude, double bottomLatitude, double rightLongitude, int zoom, final ResultMatcher matcher) { final List amenities = new ArrayList(); searchAmenitiesInProgress = true; try { @@ -901,8 +900,8 @@ public class ResourceManager { return amenities; } - public List searchAmenitiesOnThePath(List locations, double radius, SearchPoiTypeFilter filter, - ResultMatcher matcher) { + public List searchAmenitiesOnThePath(List locations, double radius, SearchPoiTypeFilter filter, + ResultMatcher matcher) { searchAmenitiesInProgress = true; final List amenities = new ArrayList(); try { @@ -921,9 +920,9 @@ public class ResourceManager { if (!filter.isEmpty()) { for (AmenityIndexRepository index : getAmenityRepositories()) { if (index.checkContainsInt( - MapUtils.get31TileNumberY(topLatitude), - MapUtils.get31TileNumberX(leftLongitude), - MapUtils.get31TileNumberY(bottomLatitude), + MapUtils.get31TileNumberY(topLatitude), + MapUtils.get31TileNumberX(leftLongitude), + MapUtils.get31TileNumberY(bottomLatitude), MapUtils.get31TileNumberX(rightLongitude))) { repos.add(index); } @@ -931,7 +930,7 @@ public class ResourceManager { if (!repos.isEmpty()) { for (AmenityIndexRepository r : repos) { List res = r.searchAmenitiesOnThePath(locations, radius, filter, matcher); - if(res != null) { + if (res != null) { amenities.addAll(res); } } @@ -943,12 +942,11 @@ public class ResourceManager { } return amenities; } - - - public boolean containsAmenityRepositoryToSearch(boolean searchByName){ + + public boolean containsAmenityRepositoryToSearch(boolean searchByName) { for (AmenityIndexRepository index : getAmenityRepositories()) { - if(searchByName){ - if(index instanceof AmenityIndexRepositoryBinary){ + if (searchByName) { + if (index instanceof AmenityIndexRepositoryBinary) { return true; } } else { @@ -957,10 +955,10 @@ public class ResourceManager { } return false; } - + public List searchAmenitiesByName(String searchQuery, - double topLatitude, double leftLongitude, double bottomLatitude, double rightLongitude, - double lat, double lon, ResultMatcher matcher) { + double topLatitude, double leftLongitude, double bottomLatitude, double rightLongitude, + double lat, double lon, ResultMatcher matcher) { List amenities = new ArrayList(); List list = new ArrayList(); int left = MapUtils.get31TileNumberX(leftLongitude); @@ -973,16 +971,16 @@ public class ResourceManager { } if (index instanceof AmenityIndexRepositoryBinary) { if (index.checkContainsInt(top, left, bottom, right)) { - if(index.checkContains(lat, lon)){ + if (index.checkContains(lat, lon)) { list.add(0, (AmenityIndexRepositoryBinary) index); } else { list.add((AmenityIndexRepositoryBinary) index); } - + } } } - + // Not using boundares results in very slow initial search if user has many maps installed // int left = 0; // int top = 0; @@ -1000,7 +998,7 @@ public class ResourceManager { return amenities; } - + public Map> searchAmenityCategoriesByName(String searchQuery, double lat, double lon) { Map> map = new LinkedHashMap>(); for (AmenityIndexRepository index : getAmenityRepositories()) { @@ -1016,17 +1014,17 @@ public class ResourceManager { public AmenityIndexRepositoryBinary getAmenityRepositoryByFileName(String filename) { return (AmenityIndexRepositoryBinary) amenityRepositories.get(filename); } - + ////////////////////////////////////////////// Working with address /////////////////////////////////////////// - - public RegionAddressRepository getRegionRepository(String name){ + + public RegionAddressRepository getRegionRepository(String name) { return addressMap.get(name); } - - public Collection getAddressRepositories(){ + + public Collection getAddressRepositories() { return addressMap.values(); } - + public Collection getFileReaders() { List fileNames = new ArrayList<>(fileReaders.keySet()); Collections.sort(fileNames, Algorithms.getStringVersionComparator()); @@ -1039,8 +1037,7 @@ public class ResourceManager { } return res; } - - + ////////////////////////////////////////////// Working with transport //////////////////////////////////////////////// private List getTransportRepositories(double topLat, double leftLon, double bottomLat, double rightLon) { @@ -1059,7 +1056,7 @@ public class ResourceManager { public List searchTransportSync(double topLat, double leftLon, double bottomLat, double rightLon, - ResultMatcher matcher) throws IOException { + ResultMatcher matcher) throws IOException { TransportStopsRouteReader readers = new TransportStopsRouteReader(getTransportRepositories(topLat, leftLon, bottomLat, rightLon)); List stops = new ArrayList<>(); @@ -1076,7 +1073,7 @@ public class ResourceManager { public List getRoutesForStop(TransportStop stop) { List rts = stop.getRoutes(); - if(rts != null) { + if (rts != null) { return rts; } return Collections.emptyList(); @@ -1086,26 +1083,26 @@ public class ResourceManager { public boolean updateRenderedMapNeeded(RotatedTileBox rotatedTileBox, DrawSettings drawSettings) { return renderer.updateMapIsNeeded(rotatedTileBox, drawSettings); } - - public void updateRendererMap(RotatedTileBox rotatedTileBox, OnMapLoadedListener mapLoadedListener){ + + public void updateRendererMap(RotatedTileBox rotatedTileBox, OnMapLoadedListener mapLoadedListener) { renderer.interruptLoadingMap(); asyncLoadingThread.requestToLoadMap(new MapLoadRequest(rotatedTileBox, mapLoadedListener)); } - - public void interruptRendering(){ + + public void interruptRendering() { renderer.interruptLoadingMap(); } - + public boolean isSearchAmenitiesInProgress() { return searchAmenitiesInProgress; } - + public MapRenderRepositories getRenderer() { return renderer; } - + ////////////////////////////////////////////// Closing methods //////////////////////////////////////////////// - + public void closeFile(String fileName) { amenityRepositories.remove(fileName); addressMap.remove(fileName); @@ -1114,12 +1111,12 @@ public class ResourceManager { travelRepositories.remove(fileName); renderer.closeConnection(fileName); BinaryMapReaderResource resource = fileReaders.remove(fileName); - if(resource != null) { + if (resource != null) { resource.close(); } - } + } - public synchronized void close(){ + public synchronized void close() { for (TilesCache tc : tilesCacheList) { tc.close(); } @@ -1130,7 +1127,7 @@ public class ResourceManager { travelRepositories.clear(); addressMap.clear(); amenityRepositories.clear(); - for(BinaryMapReaderResource res : fileReaders.values()) { + for (BinaryMapReaderResource res : fileReaders.values()) { res.close(); } fileReaders.clear(); @@ -1154,7 +1151,7 @@ public class ResourceManager { Collection fileReaders = getFileReaders(); List readers = new ArrayList<>(fileReaders.size()); for (BinaryMapReaderResource r : fileReaders) { - if (r.isUseForPublicTransport()) { + if (r.isUseForPublicTransport()) { BinaryMapIndexReader reader = r.getReader(BinaryMapReaderResourceType.TRANSPORT_ROUTING); if (reader != null) { readers.add(reader); @@ -1182,9 +1179,8 @@ public class ResourceManager { public Map getIndexFileNames() { return new LinkedHashMap(indexFileNames); } - - - public boolean containsBasemap(){ + + public boolean containsBasemap() { return !basemapFileNames.isEmpty(); } @@ -1218,13 +1214,13 @@ public class ResourceManager { } return map; } - + public synchronized void reloadTilesFromFS() { for (TilesCache tc : tilesCacheList) { tc.tilesOnFS.clear(); } } - + /// On low memory method /// public void onLowMemory() { log.info("On low memory"); @@ -1233,10 +1229,10 @@ public class ResourceManager { r.clearCache(); } renderer.clearCache(); - + System.gc(); } - + public GeoidAltitudeCorrection getGeoidAltitudeCorrection() { return geoidAltitudeCorrection; } @@ -1251,7 +1247,7 @@ public class ResourceManager { tc.clearTiles(); } } - + public IncrementalChangesManager getChangesManager() { return changesManager; } diff --git a/OsmAnd/src/net/osmand/plus/routing/cards/RouteLineColorCard.java b/OsmAnd/src/net/osmand/plus/routing/cards/RouteLineColorCard.java index f73371d643..586b7fd672 100644 --- a/OsmAnd/src/net/osmand/plus/routing/cards/RouteLineColorCard.java +++ b/OsmAnd/src/net/osmand/plus/routing/cards/RouteLineColorCard.java @@ -35,9 +35,10 @@ import net.osmand.plus.track.ColorsCard; import net.osmand.plus.track.CustomColorBottomSheet.ColorPickerListener; import net.osmand.plus.track.GradientCard; import net.osmand.plus.track.GradientScaleType; -import net.osmand.plus.widgets.MultiStateToggleButton; -import net.osmand.plus.widgets.MultiStateToggleButton.OnRadioItemClickListener; -import net.osmand.plus.widgets.MultiStateToggleButton.RadioItem; +import net.osmand.plus.widgets.multistatetoggle.RadioItem; +import net.osmand.plus.widgets.multistatetoggle.RadioItem.OnRadioItemClickListener; +import net.osmand.plus.widgets.multistatetoggle.TextToggleButton; +import net.osmand.plus.widgets.multistatetoggle.TextToggleButton.TextRadioItem; import java.util.ArrayList; import java.util.Arrays; @@ -155,17 +156,17 @@ public class RouteLineColorCard extends BaseCard implements CardListener, ColorP } private void setupRadioGroup(LinearLayout buttonsContainer) { - RadioItem day = createMapThemeButton(false); - RadioItem night = createMapThemeButton(true); + TextRadioItem day = createMapThemeButton(false); + TextRadioItem night = createMapThemeButton(true); - MultiStateToggleButton radioGroup = new MultiStateToggleButton(app, buttonsContainer, nightMode); + TextToggleButton radioGroup = new TextToggleButton(app, buttonsContainer, nightMode); radioGroup.setItems(day, night); radioGroup.setSelectedItem(!isNightMap() ? day : night); } - private RadioItem createMapThemeButton(final boolean isNight) { - RadioItem item = new RadioItem(app.getString(!isNight ? DAY_TITLE_ID : NIGHT_TITLE_ID)); + private TextRadioItem createMapThemeButton(final boolean isNight) { + TextRadioItem item = new TextRadioItem(app.getString(!isNight ? DAY_TITLE_ID : NIGHT_TITLE_ID)); item.setOnClickListener(new OnRadioItemClickListener() { @Override public boolean onRadioItemClick(RadioItem radioItem, View view) { diff --git a/OsmAnd/src/net/osmand/plus/search/QuickSearchCustomPoiFragment.java b/OsmAnd/src/net/osmand/plus/search/QuickSearchCustomPoiFragment.java index e211569f4c..2e2b43a943 100644 --- a/OsmAnd/src/net/osmand/plus/search/QuickSearchCustomPoiFragment.java +++ b/OsmAnd/src/net/osmand/plus/search/QuickSearchCustomPoiFragment.java @@ -542,7 +542,8 @@ public class QuickSearchCustomPoiFragment extends DialogFragment implements OnFi titleView.setText(textString); Set subtypes = filter.getAcceptedSubtypes(category); if (categorySelected) { - if (subtypes == null) { + LinkedHashSet poiTypes = filter.getAcceptedTypes().get(category); + if (subtypes == null || (poiTypes != null && category.getPoiTypes().size() == poiTypes.size())) { descView.setText(getString(R.string.shared_string_all)); } else { StringBuilder sb = new StringBuilder(); diff --git a/OsmAnd/src/net/osmand/plus/search/QuickSearchSubCategoriesFragment.java b/OsmAnd/src/net/osmand/plus/search/QuickSearchSubCategoriesFragment.java index 6b6f21aba1..a6dfbdbdec 100644 --- a/OsmAnd/src/net/osmand/plus/search/QuickSearchSubCategoriesFragment.java +++ b/OsmAnd/src/net/osmand/plus/search/QuickSearchSubCategoriesFragment.java @@ -101,7 +101,7 @@ public class QuickSearchSubCategoriesFragment extends BaseOsmAndDialogFragment { updateAddBtnVisibility(); } }); - if (selectAll || acceptedCategories == null) { + if (selectAll || acceptedCategories == null || poiCategory.getPoiTypes().size() == acceptedCategories.size()) { adapter.setSelectedItems(poiTypeList); selectAll = true; } else { diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/OsmandSettings.java b/OsmAnd/src/net/osmand/plus/settings/backend/OsmandSettings.java index c186146d81..2471e9f2c0 100644 --- a/OsmAnd/src/net/osmand/plus/settings/backend/OsmandSettings.java +++ b/OsmAnd/src/net/osmand/plus/settings/backend/OsmandSettings.java @@ -958,7 +958,7 @@ public class OsmandSettings { DEFAULT_SPEED.setModeDefaultValue(ApplicationMode.BICYCLE, 2.77f); DEFAULT_SPEED.setModeDefaultValue(ApplicationMode.PEDESTRIAN, 1.11f); DEFAULT_SPEED.setModeDefaultValue(ApplicationMode.BOAT, 1.38f); - DEFAULT_SPEED.setModeDefaultValue(ApplicationMode.AIRCRAFT, 40f); + DEFAULT_SPEED.setModeDefaultValue(ApplicationMode.AIRCRAFT, 200f); DEFAULT_SPEED.setModeDefaultValue(ApplicationMode.SKI, 1.38f); } @@ -1172,6 +1172,9 @@ public class OsmandSettings { public final OsmandPreference BACKUP_ACCESS_TOKEN = new StringPreference(this, "backup_access_token", "").makeGlobal(); public final OsmandPreference BACKUP_ACCESS_TOKEN_UPDATE_TIME = new StringPreference(this, "backup_access_token_update_time", "").makeGlobal(); + public final OsmandPreference FAVORITES_LAST_UPLOADED_TIME = new LongPreference(this, "favorites_last_uploaded_time", 0L).makeGlobal(); + public final OsmandPreference BACKUP_LAST_UPLOADED_TIME = new LongPreference(this, "backup_last_uploaded_time", 0L).makeGlobal(); + // this value string is synchronized with settings_pref.xml preference name public final OsmandPreference USER_OSM_BUG_NAME = new StringPreference(this, "user_osm_bug_name", "NoName/OsmAnd").makeGlobal().makeShared(); @@ -2886,4 +2889,4 @@ public class OsmandSettings { setPreference(QUICK_ACTION.getId(), actionState, mode); } } -} \ No newline at end of file +} diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/backup/FileSettingsItem.java b/OsmAnd/src/net/osmand/plus/settings/backend/backup/FileSettingsItem.java index 88c72beddc..53b6ec0500 100644 --- a/OsmAnd/src/net/osmand/plus/settings/backend/backup/FileSettingsItem.java +++ b/OsmAnd/src/net/osmand/plus/settings/backend/backup/FileSettingsItem.java @@ -7,6 +7,7 @@ import androidx.annotation.Nullable; import net.osmand.IndexConstants; import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; +import net.osmand.plus.download.SrtmDownloadItem; import net.osmand.util.Algorithms; import org.json.JSONException; @@ -92,7 +93,7 @@ public class FileSettingsItem extends StreamSettingsItem { case OTHER: break; case SRTM_MAP: - if (name.endsWith(IndexConstants.BINARY_SRTM_MAP_INDEX_EXT)) { + if (SrtmDownloadItem.isSrtmFile(name)) { return subtype; } break; @@ -257,6 +258,8 @@ public class FileSettingsItem extends StreamSettingsItem { prefix = oldPath.substring(0, oldPath.lastIndexOf(IndexConstants.BINARY_WIKI_MAP_INDEX_EXT)); } else if (oldPath.endsWith(IndexConstants.BINARY_SRTM_MAP_INDEX_EXT)) { prefix = oldPath.substring(0, oldPath.lastIndexOf(IndexConstants.BINARY_SRTM_MAP_INDEX_EXT)); + } else if (oldPath.endsWith(IndexConstants.BINARY_SRTM_FEET_MAP_INDEX_EXT)) { + prefix = oldPath.substring(0, oldPath.lastIndexOf(IndexConstants.BINARY_SRTM_FEET_MAP_INDEX_EXT)); } else if (oldPath.endsWith(IndexConstants.BINARY_ROAD_MAP_INDEX_EXT)) { prefix = oldPath.substring(0, oldPath.lastIndexOf(IndexConstants.BINARY_ROAD_MAP_INDEX_EXT)); } else { diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/backup/HistoryMarkersSettingsItem.java b/OsmAnd/src/net/osmand/plus/settings/backend/backup/HistoryMarkersSettingsItem.java index 9cbb536177..ba0c3d7dc0 100644 --- a/OsmAnd/src/net/osmand/plus/settings/backend/backup/HistoryMarkersSettingsItem.java +++ b/OsmAnd/src/net/osmand/plus/settings/backend/backup/HistoryMarkersSettingsItem.java @@ -12,7 +12,7 @@ import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; import net.osmand.plus.mapmarkers.MapMarker; import net.osmand.plus.mapmarkers.MapMarkersDbHelper; -import net.osmand.plus.itinerary.ItineraryGroup; +import net.osmand.plus.mapmarkers.MapMarkersGroup; import net.osmand.plus.mapmarkers.MapMarkersHelper; import net.osmand.plus.settings.backend.ExportSettingsType; import net.osmand.util.Algorithms; @@ -46,7 +46,7 @@ public class HistoryMarkersSettingsItem extends CollectionSettingsItem(app.getItineraryHelper().getMapMarkersFromDefaultGroups(true)); + existingItems = new ArrayList<>(markersHelper.getMapMarkersFromDefaultGroups(true)); } @NonNull @@ -122,10 +122,10 @@ public class HistoryMarkersSettingsItem extends CollectionSettingsItem { protected void init() { super.init(); markersHelper = app.getMapMarkersHelper(); - existingItems = new ArrayList<>(app.getItineraryHelper().getMapMarkersFromDefaultGroups(false)); + existingItems = new ArrayList<>(markersHelper.getMapMarkersFromDefaultGroups(false)); } @NonNull @@ -122,10 +122,10 @@ public class MarkersSettingsItem extends CollectionSettingsItem { } } - public ItineraryGroup getMarkersGroup() { + public MapMarkersGroup getMarkersGroup() { String name = app.getString(R.string.map_markers); String groupId = ExportSettingsType.ACTIVE_MARKERS.name(); - ItineraryGroup markersGroup = new ItineraryGroup(groupId, name, ItineraryGroup.ANY_TYPE); + MapMarkersGroup markersGroup = new MapMarkersGroup(groupId, name, MapMarkersGroup.ANY_TYPE); markersGroup.setMarkers(items); return markersGroup; } diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/backup/PoiUiFiltersSettingsItem.java b/OsmAnd/src/net/osmand/plus/settings/backend/backup/PoiUiFiltersSettingsItem.java index 16be1f26bc..e0a0ced9a8 100644 --- a/OsmAnd/src/net/osmand/plus/settings/backend/backup/PoiUiFiltersSettingsItem.java +++ b/OsmAnd/src/net/osmand/plus/settings/backend/backup/PoiUiFiltersSettingsItem.java @@ -10,6 +10,7 @@ import com.google.gson.reflect.TypeToken; import net.osmand.osm.MapPoiTypes; import net.osmand.osm.PoiCategory; +import net.osmand.osm.PoiType; import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; import net.osmand.plus.poi.PoiUIFilter; @@ -148,7 +149,21 @@ public class PoiUiFiltersSettingsItem extends CollectionSettingsItem> acceptedTypes = filter.getAcceptedTypes(); + for (PoiCategory category : acceptedTypes.keySet()) { + LinkedHashSet poiTypes = acceptedTypes.get(category); + if (poiTypes == null) { + poiTypes = new LinkedHashSet<>(); + List types = category.getPoiTypes(); + for (PoiType poiType : types) { + poiTypes.add(poiType.getKeyName()); + } + acceptedTypes.put(category, poiTypes); + } + } + + jsonObject.put("acceptedTypes", gson.toJson(acceptedTypes, type)); jsonArray.put(jsonObject); } json.put("items", jsonArray); diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/backup/SettingsHelper.java b/OsmAnd/src/net/osmand/plus/settings/backend/backup/SettingsHelper.java index 61a429f05f..61cfd43cc1 100644 --- a/OsmAnd/src/net/osmand/plus/settings/backend/backup/SettingsHelper.java +++ b/OsmAnd/src/net/osmand/plus/settings/backend/backup/SettingsHelper.java @@ -30,10 +30,11 @@ import net.osmand.plus.helpers.AvoidSpecificRoads.AvoidRoadInfo; import net.osmand.plus.helpers.FileNameTranslationHelper; import net.osmand.plus.helpers.GpxUiHelper; import net.osmand.plus.helpers.GpxUiHelper.GPXInfo; +import net.osmand.plus.helpers.LocaleHelper; import net.osmand.plus.helpers.SearchHistoryHelper; import net.osmand.plus.helpers.SearchHistoryHelper.HistoryEntry; import net.osmand.plus.mapmarkers.MapMarker; -import net.osmand.plus.itinerary.ItineraryGroup; +import net.osmand.plus.mapmarkers.MapMarkersGroup; import net.osmand.plus.onlinerouting.engine.OnlineRoutingEngine; import net.osmand.plus.osmedit.OpenstreetmapPoint; import net.osmand.plus.osmedit.OsmEditingPlugin; @@ -605,19 +606,19 @@ public class SettingsHelper { myPlacesItems.put(ExportSettingsType.MULTIMEDIA_NOTES, files); } } - List mapMarkers = app.getItineraryHelper().getMapMarkersFromDefaultGroups(false); + List mapMarkers = app.getMapMarkersHelper().getMapMarkersFromDefaultGroups(false); if (!mapMarkers.isEmpty()) { String name = app.getString(R.string.map_markers); String groupId = ExportSettingsType.ACTIVE_MARKERS.name(); - ItineraryGroup markersGroup = new ItineraryGroup(groupId, name, ItineraryGroup.ANY_TYPE); + MapMarkersGroup markersGroup = new MapMarkersGroup(groupId, name, MapMarkersGroup.ANY_TYPE); markersGroup.setMarkers(mapMarkers); myPlacesItems.put(ExportSettingsType.ACTIVE_MARKERS, Collections.singletonList(markersGroup)); } - List markersHistory = app.getItineraryHelper().getMapMarkersFromDefaultGroups(true); + List markersHistory = app.getMapMarkersHelper().getMapMarkersFromDefaultGroups(true); if (!markersHistory.isEmpty()) { String name = app.getString(R.string.shared_string_history); String groupId = ExportSettingsType.HISTORY_MARKERS.name(); - ItineraryGroup markersGroup = new ItineraryGroup(groupId, name, ItineraryGroup.ANY_TYPE); + MapMarkersGroup markersGroup = new MapMarkersGroup(groupId, name, MapMarkersGroup.ANY_TYPE); markersGroup.setMarkers(markersHistory); myPlacesItems.put(ExportSettingsType.HISTORY_MARKERS, Collections.singletonList(markersGroup)); } @@ -720,8 +721,8 @@ public class SettingsHelper { List favoriteGroups = new ArrayList<>(); List osmNotesPointList = new ArrayList<>(); List osmEditsPointList = new ArrayList<>(); - List markersGroups = new ArrayList<>(); - List markersHistoryGroups = new ArrayList<>(); + List markersGroups = new ArrayList<>(); + List markersHistoryGroups = new ArrayList<>(); List historyEntries = new ArrayList<>(); List onlineRoutingEngines = new ArrayList<>(); @@ -755,12 +756,12 @@ public class SettingsHelper { osmEditsPointList.add((OpenstreetmapPoint) object); } else if (object instanceof FavoriteGroup) { favoriteGroups.add((FavoriteGroup) object); - } else if (object instanceof ItineraryGroup) { - ItineraryGroup markersGroup = (ItineraryGroup) object; + } else if (object instanceof MapMarkersGroup) { + MapMarkersGroup markersGroup = (MapMarkersGroup) object; if (ExportSettingsType.ACTIVE_MARKERS.name().equals(markersGroup.getId())) { - markersGroups.add((ItineraryGroup) object); + markersGroups.add((MapMarkersGroup) object); } else if (ExportSettingsType.HISTORY_MARKERS.name().equals(markersGroup.getId())) { - markersHistoryGroups.add((ItineraryGroup) object); + markersHistoryGroups.add((MapMarkersGroup) object); } } else if (object instanceof HistoryEntry) { historyEntries.add((HistoryEntry) object); @@ -812,7 +813,7 @@ public class SettingsHelper { } if (!markersGroups.isEmpty()) { List mapMarkers = new ArrayList<>(); - for (ItineraryGroup group : markersGroups) { + for (MapMarkersGroup group : markersGroups) { mapMarkers.addAll(group.getMarkers()); } MarkersSettingsItem baseItem = getBaseItem(SettingsItemType.ACTIVE_MARKERS, MarkersSettingsItem.class, settingsItems); @@ -820,7 +821,7 @@ public class SettingsHelper { } if (!markersHistoryGroups.isEmpty()) { List mapMarkers = new ArrayList<>(); - for (ItineraryGroup group : markersHistoryGroups) { + for (MapMarkersGroup group : markersHistoryGroups) { mapMarkers.addAll(group.getMarkers()); } HistoryMarkersSettingsItem baseItem = getBaseItem(SettingsItemType.HISTORY_MARKERS, HistoryMarkersSettingsItem.class, settingsItems); @@ -910,8 +911,8 @@ public class SettingsHelper { List notesPointList = new ArrayList<>(); List editsPointList = new ArrayList<>(); List favoriteGroups = new ArrayList<>(); - List markersGroups = new ArrayList<>(); - List markersHistoryGroups = new ArrayList<>(); + List markersGroups = new ArrayList<>(); + List markersHistoryGroups = new ArrayList<>(); List historyEntries = new ArrayList<>(); List onlineRoutingEngines = new ArrayList<>(); diff --git a/OsmAnd/src/net/osmand/plus/settings/bottomsheets/AnnouncementTimeBottomSheet.java b/OsmAnd/src/net/osmand/plus/settings/bottomsheets/AnnouncementTimeBottomSheet.java index bbec0c6211..c697213c30 100644 --- a/OsmAnd/src/net/osmand/plus/settings/bottomsheets/AnnouncementTimeBottomSheet.java +++ b/OsmAnd/src/net/osmand/plus/settings/bottomsheets/AnnouncementTimeBottomSheet.java @@ -1,26 +1,24 @@ package net.osmand.plus.settings.bottomsheets; -import android.graphics.drawable.ClipDrawable; -import android.graphics.drawable.Drawable; -import android.graphics.drawable.GradientDrawable; -import android.graphics.drawable.LayerDrawable; import android.os.Bundle; -import android.view.Gravity; import android.view.View; +import android.view.View.OnClickListener; import android.widget.ImageView; -import android.widget.SeekBar; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.core.content.ContextCompat; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; +import com.google.android.material.slider.Slider; +import com.google.android.material.slider.Slider.OnChangeListener; + import net.osmand.PlatformUtil; import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; import net.osmand.plus.UiUtilities; import net.osmand.plus.base.bottomsheetmenu.BaseBottomSheetItem; +import net.osmand.plus.base.bottomsheetmenu.BaseBottomSheetItem.Builder; import net.osmand.plus.helpers.AndroidUiHelper; import net.osmand.plus.routing.data.AnnounceTimeDistances; import net.osmand.plus.settings.backend.ApplicationMode; @@ -33,8 +31,7 @@ import org.apache.commons.logging.Log; import static net.osmand.plus.settings.bottomsheets.SingleSelectPreferenceBottomSheet.SELECTED_ENTRY_INDEX_KEY; -public class AnnouncementTimeBottomSheet extends BasePreferenceBottomSheet - implements SeekBar.OnSeekBarChangeListener { +public class AnnouncementTimeBottomSheet extends BasePreferenceBottomSheet { public static final String TAG = AnnouncementTimeBottomSheet.class.getSimpleName(); private static final Log LOG = PlatformUtil.getLog(AnnouncementTimeBottomSheet.class); @@ -46,7 +43,7 @@ public class AnnouncementTimeBottomSheet extends BasePreferenceBottomSheet private int selectedEntryIndex = -1; private TextViewEx tvSeekBarLabel; - private SeekBar seekBarArrival; + private Slider slider; private ImageView ivArrow; private TextViewEx tvIntervalsDescr; @@ -110,22 +107,6 @@ public class AnnouncementTimeBottomSheet extends BasePreferenceBottomSheet dismiss(); } - @Override - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - if (progress != selectedEntryIndex) { - selectedEntryIndex = progress; - updateViews(); - } - } - - @Override - public void onStartTrackingTouch(SeekBar seekBar) { - } - - @Override - public void onStopTrackingTouch(SeekBar seekBar) { - } - private ListPreferenceEx getListPreference() { return (ListPreferenceEx) getPreference(); } @@ -135,30 +116,40 @@ public class AnnouncementTimeBottomSheet extends BasePreferenceBottomSheet .inflate(R.layout.bottom_sheet_announcement_time, null); tvSeekBarLabel = rootView.findViewById(R.id.tv_seek_bar_label); - seekBarArrival = rootView.findViewById(R.id.seek_bar_arrival); + slider = rootView.findViewById(R.id.arrival_slider); ivArrow = rootView.findViewById(R.id.iv_arrow); tvIntervalsDescr = rootView.findViewById(R.id.tv_interval_descr); + int appModeColor = getAppMode().getProfileColor(nightMode); - setProfileColorToSeekBar(); - seekBarArrival.setOnSeekBarChangeListener(this); - seekBarArrival.setProgress(selectedEntryIndex); - seekBarArrival.setMax(listPreference.getEntries().length - 1); - rootView.findViewById(R.id.description_container).setOnClickListener(new View.OnClickListener() { + slider.setValue(selectedEntryIndex); + slider.setValueFrom(0); + slider.setValueTo(listPreference.getEntries().length - 1); + slider.setStepSize(1); + slider.addOnChangeListener(new OnChangeListener() { + @Override + public void onValueChange(@NonNull Slider slider, float value, boolean fromUser) { + int intValue = (int) value; + if (intValue != selectedEntryIndex) { + selectedEntryIndex = intValue; + updateViews(); + } + } + }); + UiUtilities.setupSlider(slider, nightMode, appModeColor, true); + rootView.findViewById(R.id.description_container).setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { toggleDescriptionVisibility(); } }); - return new BaseBottomSheetItem.Builder() + return new Builder() .setCustomView(rootView) .create(); } private void updateViews() { - seekBarArrival.setProgress(selectedEntryIndex); tvSeekBarLabel.setText(listPreference.getEntries()[selectedEntryIndex]); - float value = (float) listPreference.getEntryValues()[selectedEntryIndex]; announceTimeDistances.setArrivalDistances(value); tvIntervalsDescr.setText(announceTimeDistances.getIntervalsDescription(app)); @@ -170,31 +161,6 @@ public class AnnouncementTimeBottomSheet extends BasePreferenceBottomSheet AndroidUiHelper.updateVisibility(tvIntervalsDescr, !collapsed); } - private void setProfileColorToSeekBar() { - int color = getAppMode().getProfileColor(nightMode); - - LayerDrawable seekBarProgressLayer = - (LayerDrawable) ContextCompat.getDrawable(app, R.drawable.seekbar_progress_announcement_time); - - GradientDrawable background = (GradientDrawable) seekBarProgressLayer.findDrawableByLayerId(R.id.background); - background.setColor(color); - background.setAlpha(70); - - GradientDrawable progress = (GradientDrawable) seekBarProgressLayer.findDrawableByLayerId(R.id.progress); - progress.setColor(color); - Drawable clippedProgress = new ClipDrawable(progress, Gravity.CENTER_VERTICAL | Gravity.START, 1); - - seekBarArrival.setProgressDrawable(new LayerDrawable(new Drawable[] { - background, clippedProgress - })); - - LayerDrawable seekBarThumpLayer = - (LayerDrawable) ContextCompat.getDrawable(app, R.drawable.seekbar_thumb_announcement_time); - GradientDrawable thump = (GradientDrawable) seekBarThumpLayer.findDrawableByLayerId(R.id.thump); - thump.setColor(color); - seekBarArrival.setThumb(thump); - } - public static void showInstance(@NonNull FragmentManager fm, String prefKey, Fragment target, @Nullable ApplicationMode appMode, boolean usedOnMap) { try { diff --git a/OsmAnd/src/net/osmand/plus/settings/bottomsheets/BooleanPreferenceBottomSheet.java b/OsmAnd/src/net/osmand/plus/settings/bottomsheets/BooleanPreferenceBottomSheet.java index 858e32c962..1fd19e00c1 100644 --- a/OsmAnd/src/net/osmand/plus/settings/bottomsheets/BooleanPreferenceBottomSheet.java +++ b/OsmAnd/src/net/osmand/plus/settings/bottomsheets/BooleanPreferenceBottomSheet.java @@ -134,6 +134,7 @@ public class BooleanPreferenceBottomSheet extends BasePreferenceBottomSheet { public static void updateCustomButtonView(OsmandApplication app, ApplicationMode mode, View customView, boolean checked, boolean nightMode) { Context themedCtx = UiUtilities.getThemedContext(app, nightMode); + boolean isLayoutRtl = AndroidUtils.isLayoutRtl(themedCtx); LinearLayout buttonView = customView.findViewById(R.id.button_container); int bgColor; @@ -149,10 +150,11 @@ public class BooleanPreferenceBottomSheet extends BasePreferenceBottomSheet { ContextCompat.getColor(app, getActivePrimaryColorId(nightMode)), checked ? 0.3f : 0.5f); } - int bgResId = R.drawable.rectangle_rounded_right; + int bgResId = isLayoutRtl ? R.drawable.rectangle_rounded_left : R.drawable.rectangle_rounded_right; if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) { - int selectableResId = R.drawable.ripple_rectangle_rounded_right; - + int selectableResId = isLayoutRtl ? + R.drawable.ripple_rectangle_rounded_left : + R.drawable.ripple_rectangle_rounded_right; Drawable bgDrawable = app.getUIUtilities().getPaintedIcon(bgResId, bgColor); Drawable selectable = app.getUIUtilities().getPaintedIcon(selectableResId, selectedColor); Drawable[] layers = {bgDrawable, selectable}; diff --git a/OsmAnd/src/net/osmand/plus/settings/datastorage/DataStorageHelper.java b/OsmAnd/src/net/osmand/plus/settings/datastorage/DataStorageHelper.java index 67fd73897e..763f8ff996 100644 --- a/OsmAnd/src/net/osmand/plus/settings/datastorage/DataStorageHelper.java +++ b/OsmAnd/src/net/osmand/plus/settings/datastorage/DataStorageHelper.java @@ -200,7 +200,7 @@ public class DataStorageHelper { terrainMemory = MemoryItem.builder() .setKey(TERRAIN_MEMORY) - .setExtensions(IndexConstants.BINARY_SRTM_MAP_INDEX_EXT) + .setExtensions(IndexConstants.BINARY_SRTM_MAP_INDEX_EXT, IndexConstants.BINARY_SRTM_FEET_MAP_INDEX_EXT) .setDirectories( createDirectory(SRTM_INDEX_DIR, true, EXTENSIONS, true), createDirectory(TILES_INDEX_DIR, false, PREFIX, false)) @@ -323,7 +323,7 @@ public class DataStorageHelper { public interface UpdateMemoryInfoUIAdapter { void onMemoryInfoUpdate(); - + void onFinishUpdating(String tag); } diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/BaseSettingsFragment.java b/OsmAnd/src/net/osmand/plus/settings/fragments/BaseSettingsFragment.java index 05c4c7e323..73c62eacfe 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/BaseSettingsFragment.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/BaseSettingsFragment.java @@ -492,8 +492,7 @@ public abstract class BaseSettingsFragment extends PreferenceFragmentCompat impl } }); if (closeButton instanceof ImageView) { - UiUtilities.rotateImageByLayoutDirection( - (ImageView) closeButton, AndroidUtils.getLayoutDirection(app)); + UiUtilities.rotateImageByLayoutDirection((ImageView) closeButton); } } diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/BaseSettingsListFragment.java b/OsmAnd/src/net/osmand/plus/settings/fragments/BaseSettingsListFragment.java index 6e2129deab..b7c5c6a0f9 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/BaseSettingsListFragment.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/BaseSettingsListFragment.java @@ -36,7 +36,6 @@ import net.osmand.plus.settings.fragments.ExportSettingsAdapter.OnItemSelectedLi import net.osmand.plus.widgets.TextViewEx; import net.osmand.util.Algorithms; -import java.io.File; import java.util.ArrayList; import java.util.EnumMap; import java.util.LinkedHashMap; @@ -221,8 +220,7 @@ public abstract class BaseSettingsListFragment extends BaseOsmAndFragment implem if (calculatedSize != 0) { selectedItemsSize.setText(AndroidUtils.formatSize(app, calculatedSize)); - File dir = app.getAppPath("").getParentFile(); - long availableSizeBytes = AndroidUtils.getAvailableSpace(dir); + long availableSizeBytes = AndroidUtils.getAvailableSpace(app); if (calculatedSize > availableSizeBytes) { String availableSize = AndroidUtils.formatSize(app, availableSizeBytes); availableSpaceDescr.setText(getString(R.string.export_not_enough_space_descr, availableSize)); diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/ExportItemsBottomSheet.java b/OsmAnd/src/net/osmand/plus/settings/fragments/ExportItemsBottomSheet.java index dfe3771416..c3b74af4fd 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/ExportItemsBottomSheet.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/ExportItemsBottomSheet.java @@ -36,11 +36,12 @@ import net.osmand.plus.base.bottomsheetmenu.BottomSheetItemWithCompoundButton.Bu import net.osmand.plus.base.bottomsheetmenu.BottomSheetItemWithDescription; import net.osmand.plus.base.bottomsheetmenu.SimpleBottomSheetItem; import net.osmand.plus.base.bottomsheetmenu.simpleitems.SimpleDividerItem; +import net.osmand.plus.download.SrtmDownloadItem; import net.osmand.plus.helpers.AvoidSpecificRoads.AvoidRoadInfo; import net.osmand.plus.helpers.FileNameTranslationHelper; import net.osmand.plus.helpers.GpxUiHelper; import net.osmand.plus.helpers.SearchHistoryHelper.HistoryEntry; -import net.osmand.plus.itinerary.ItineraryGroup; +import net.osmand.plus.mapmarkers.MapMarkersGroup; import net.osmand.plus.onlinerouting.engine.OnlineRoutingEngine; import net.osmand.plus.osmedit.OpenstreetmapPoint; import net.osmand.plus.osmedit.OsmEditingPlugin; @@ -277,7 +278,7 @@ public class ExportItemsBottomSheet extends MenuBottomSheetDialogFragment { } private String setupDescription(View view) { - TextView description = view.findViewById(R.id.description); + TextView description = view.findViewById(R.id.title_description); if (type == ExportSettingsType.FAVORITES) { description.setText(R.string.select_groups_for_import); } else { @@ -376,8 +377,8 @@ public class ExportItemsBottomSheet extends MenuBottomSheetDialogFragment { GlobalSettingsItem globalSettingsItem = (GlobalSettingsItem) object; item.setTitle(globalSettingsItem.getPublicName(app)); item.setIcon(uiUtilities.getIcon(R.drawable.ic_action_settings, getItemIconColor(object))); - } else if (object instanceof ItineraryGroup) { - ItineraryGroup markersGroup = (ItineraryGroup) object; + } else if (object instanceof MapMarkersGroup) { + MapMarkersGroup markersGroup = (MapMarkersGroup) object; if (ExportSettingsType.ACTIVE_MARKERS.name().equals(markersGroup.getId())) { item.setTitle(getString(R.string.map_markers)); item.setIcon(uiUtilities.getIcon(R.drawable.ic_action_flag, getItemIconColor(object))); @@ -530,7 +531,7 @@ public class ExportItemsBottomSheet extends MenuBottomSheetDialogFragment { return getString(R.string.download_roads_only_item); } else if (file.getName().endsWith(IndexConstants.BINARY_WIKI_MAP_INDEX_EXT)) { return getString(R.string.download_wikipedia_maps); - } else if (file.getName().endsWith(IndexConstants.BINARY_SRTM_MAP_INDEX_EXT)) { + } else if (SrtmDownloadItem.isSrtmFile(file.getName())) { return getString(R.string.download_srtm_maps); } else if (file.getName().endsWith(IndexConstants.BINARY_MAP_INDEX_EXT)) { return getString(R.string.download_regular_maps); diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/ExportSettingsAdapter.java b/OsmAnd/src/net/osmand/plus/settings/fragments/ExportSettingsAdapter.java index 360f786b77..6b6a85035d 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/ExportSettingsAdapter.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/ExportSettingsAdapter.java @@ -18,7 +18,7 @@ import net.osmand.plus.UiUtilities; import net.osmand.plus.activities.OsmandBaseExpandableListAdapter; import net.osmand.plus.helpers.AndroidUiHelper; import net.osmand.plus.helpers.FontCache; -import net.osmand.plus.itinerary.ItineraryGroup; +import net.osmand.plus.mapmarkers.MapMarkersGroup; import net.osmand.plus.settings.backend.ExportSettingsCategory; import net.osmand.plus.settings.backend.ExportSettingsType; import net.osmand.plus.settings.backend.backup.FileSettingsItem; @@ -311,8 +311,8 @@ public class ExportSettingsAdapter extends OsmandBaseExpandableListAdapter { itemsSize += ((FileSettingsItem) object).getSize(); } else if (object instanceof File) { itemsSize += ((File) object).length(); - } else if (object instanceof ItineraryGroup) { - int selectedMarkers = ((ItineraryGroup) object).getMarkers().size(); + } else if (object instanceof MapMarkersGroup) { + int selectedMarkers = ((MapMarkersGroup) object).getMarkers().size(); String itemsDescr = app.getString(R.string.shared_string_items); return app.getString(R.string.ltr_or_rtl_combine_via_colon, itemsDescr, selectedMarkers); } diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/PurchasesFragment.java b/OsmAnd/src/net/osmand/plus/settings/fragments/PurchasesFragment.java index 461f51e14d..9c60348d65 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/PurchasesFragment.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/PurchasesFragment.java @@ -10,6 +10,11 @@ import android.widget.ImageButton; import android.widget.ImageView; import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.FragmentActivity; +import androidx.fragment.app.FragmentManager; + import com.google.android.material.appbar.AppBarLayout; import net.osmand.AndroidUtils; @@ -30,11 +35,6 @@ import net.osmand.util.Algorithms; import org.apache.commons.logging.Log; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.fragment.app.FragmentActivity; -import androidx.fragment.app.FragmentManager; - public class PurchasesFragment extends BaseOsmAndFragment implements InAppPurchaseListener, OnFragmentInteractionListener { private static final Log log = PlatformUtil.getLog(PurchasesFragment.class); @@ -152,6 +152,7 @@ public class PurchasesFragment extends BaseOsmAndFragment implements InAppPurcha } }); ImageButton backButton = toolbar.findViewById(R.id.close_button); + UiUtilities.rotateImageByLayoutDirection(backButton); backButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -197,9 +198,7 @@ public class PurchasesFragment extends BaseOsmAndFragment implements InAppPurcha @Override public void onSearchResult(CountrySelectionFragment.CountryItem name) { - if (subscriptionsCard != null) { - subscriptionsCard.onSupportRegionSelected(name); - } + } @Override diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/SubscriptionsCard.java b/OsmAnd/src/net/osmand/plus/settings/fragments/SubscriptionsCard.java index 6b7ba26980..0957969ccd 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/SubscriptionsCard.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/SubscriptionsCard.java @@ -5,30 +5,26 @@ import android.net.Uri; import android.view.View; import android.view.ViewGroup; import android.widget.LinearLayout; -import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; import net.osmand.AndroidUtils; import net.osmand.plus.R; import net.osmand.plus.activities.MapActivity; import net.osmand.plus.helpers.AndroidUiHelper; import net.osmand.plus.inapp.InAppPurchaseHelper; -import net.osmand.plus.liveupdates.CountrySelectionFragment; import net.osmand.plus.liveupdates.LiveUpdatesFragment; import net.osmand.plus.liveupdates.OsmLiveActivity; import net.osmand.plus.routepreparationmenu.cards.BaseCard; -import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.util.Algorithms; -import androidx.annotation.NonNull; -import androidx.fragment.app.Fragment; - public class SubscriptionsCard extends BaseCard { private static final String PLAY_STORE_SUBSCRIPTION_URL = "https://play.google.com/store/account/subscriptions"; private static final String PLAY_STORE_SUBSCRIPTION_DEEPLINK_URL = "https://play.google.com/store/account/subscriptions?sku=%s&package=%s"; private Fragment target; - private CountrySelectionFragment countrySelectionFragment = new CountrySelectionFragment(); private SubscriptionsListCard subscriptionsListCard; private InAppPurchaseHelper purchaseHelper; @@ -54,7 +50,6 @@ public class SubscriptionsCard extends BaseCard { } updateSubscriptionsListCard(); - setupSupportRegion(); LinearLayout reportContainer = view.findViewById(R.id.report_container); reportContainer.setOnClickListener(new View.OnClickListener() { @@ -99,26 +94,6 @@ public class SubscriptionsCard extends BaseCard { } } - private void setupSupportRegion() { - String region = LiveUpdatesFragment.getSupportRegionName(app, purchaseHelper); - String header = LiveUpdatesFragment.getSupportRegionHeader(app, region); - TextView supportRegionHeader = view.findViewById(R.id.support_region_header); - TextView supportRegion = view.findViewById(R.id.support_region); - supportRegionHeader.setText(header); - supportRegion.setText(region); - - View supportRegionContainer = view.findViewById(R.id.support_region_container); - supportRegionContainer.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - CountrySelectionFragment countryCountrySelectionFragment = countrySelectionFragment; - countryCountrySelectionFragment.show(target.getChildFragmentManager(), CountrySelectionFragment.TAG); - } - }); - - countrySelectionFragment.initCountries(app); - } - private String getSubscriptionUrl() { InAppPurchaseHelper purchaseHelper = app.getInAppPurchaseHelper(); if (purchaseHelper != null && purchaseHelper.getFullVersion() != null) { @@ -129,17 +104,4 @@ public class SubscriptionsCard extends BaseCard { return PLAY_STORE_SUBSCRIPTION_URL; } } - - public void onSupportRegionSelected(CountrySelectionFragment.CountryItem selectedCountryItem) { - String countryName = selectedCountryItem != null ? selectedCountryItem.getLocalName() : ""; - String countryDownloadName = selectedCountryItem != null ? - selectedCountryItem.getDownloadName() : OsmandSettings.BILLING_USER_DONATION_WORLD_PARAMETER; - - TextView supportRegionHeader = view.findViewById(R.id.support_region_header); - TextView supportRegion = view.findViewById(R.id.support_region); - supportRegionHeader.setText(LiveUpdatesFragment.getSupportRegionHeader(app, countryName)); - supportRegion.setText(countryName); - app.getSettings().BILLING_USER_COUNTRY.set(countryName); - app.getSettings().BILLING_USER_COUNTRY_DOWNLOAD_NAME.set(countryDownloadName); - } } \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/SubscriptionsListCard.java b/OsmAnd/src/net/osmand/plus/settings/fragments/SubscriptionsListCard.java index 645a68b037..d4f56cdc76 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/SubscriptionsListCard.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/SubscriptionsListCard.java @@ -7,7 +7,6 @@ import android.view.ViewGroup; import android.widget.TextView; import net.osmand.AndroidUtils; -import net.osmand.Period; import net.osmand.plus.R; import net.osmand.plus.UiUtilities; import net.osmand.plus.activities.MapActivity; @@ -20,11 +19,10 @@ import net.osmand.plus.routepreparationmenu.cards.BaseCard; import net.osmand.util.Algorithms; import java.text.SimpleDateFormat; -import java.util.Calendar; -import java.util.Date; import java.util.List; import java.util.Locale; +import androidx.annotation.DrawableRes; import androidx.annotation.NonNull; public class SubscriptionsListCard extends BaseCard { @@ -60,7 +58,13 @@ public class SubscriptionsListCard extends BaseCard { for (int i = 0; i < subscriptions.size(); i++) { InAppSubscription subscription = subscriptions.get(i); SubscriptionState state = subscription.getState(); - boolean autoRenewed = state == SubscriptionState.ACTIVE || state == SubscriptionState.IN_GRACE_PERIOD; + boolean autoRenewing = false; + if (subscription.isPurchased() && subscription.getPurchaseInfo() != null) { + autoRenewing = subscription.getPurchaseInfo().isAutoRenewing(); + state = SubscriptionState.ACTIVE; + } else if (state != SubscriptionState.UNDEFINED) { + autoRenewing = state == SubscriptionState.ACTIVE || state == SubscriptionState.IN_GRACE_PERIOD; + } View card = inflater.inflate(R.layout.subscription_layout, null, false); ((ViewGroup) view).addView(card); @@ -72,11 +76,18 @@ public class SubscriptionsListCard extends BaseCard { AndroidUiHelper.updateVisibility(subscriptionPeriod, true); } - if (autoRenewed) { + if (autoRenewing) { TextView nextBillingDate = card.findViewById(R.id.next_billing_date); - String date = getHumanDate(subscription.getPurchaseTime(), subscription.getSubscriptionPeriod()); - if (!Algorithms.isEmpty(date)) { - nextBillingDate.setText(app.getString(R.string.next_billing_date, date)); + String expiredTimeStr = null; + long expiredTime = subscription.getExpireTime(); + if (expiredTime == 0) { + expiredTime = subscription.getCalculatedExpiredTime(); + } + if (expiredTime > 0) { + expiredTimeStr = dateFormat.format(expiredTime); + } + if (!Algorithms.isEmpty(expiredTimeStr)) { + nextBillingDate.setText(app.getString(R.string.next_billing_date, expiredTimeStr)); AndroidUiHelper.updateVisibility(nextBillingDate, true); } } else { @@ -102,7 +113,7 @@ public class SubscriptionsListCard extends BaseCard { TextView status = card.findViewById(R.id.status); status.setText(app.getString(state.getStringRes())); - AndroidUtils.setBackground(status, app.getUIUtilities().getIcon(state.getBackgroundRes())); + AndroidUtils.setBackground(status, app.getUIUtilities().getIcon(getBackgroundRes(state))); int dividerLayout = i + 1 == subscriptions.size() ? R.layout.simple_divider_item : R.layout.divider_half_item; View divider = inflater.inflate(dividerLayout, (ViewGroup) view, false); @@ -110,15 +121,9 @@ public class SubscriptionsListCard extends BaseCard { } } - private String getHumanDate(long time, Period period) { - if (period == null || period.getUnit() == null) { - return ""; - } - Date date = new Date(time); - Calendar calendar = Calendar.getInstance(); - calendar.setTime(date); - calendar.add(period.getUnit().getCalendarIdx(), period.getNumberOfUnits()); - date = calendar.getTime(); - return dateFormat.format(date); + @DrawableRes + private int getBackgroundRes(@NonNull SubscriptionState state) { + return state == SubscriptionState.ACTIVE || state == SubscriptionState.IN_GRACE_PERIOD + ? R.drawable.bg_osmand_live_active : R.drawable.bg_osmand_live_cancelled; } } \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/TroubleshootingOrPurchasingCard.java b/OsmAnd/src/net/osmand/plus/settings/fragments/TroubleshootingOrPurchasingCard.java index f0ed9d8968..6758292334 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/TroubleshootingOrPurchasingCard.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/TroubleshootingOrPurchasingCard.java @@ -7,20 +7,22 @@ import android.text.Spannable; import android.text.SpannableString; import android.text.style.StyleSpan; import android.view.View; +import android.widget.ImageView; import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.cardview.widget.CardView; +import androidx.core.content.ContextCompat; + import net.osmand.AndroidUtils; import net.osmand.plus.R; +import net.osmand.plus.UiUtilities; import net.osmand.plus.activities.MapActivity; import net.osmand.plus.chooseplan.ChoosePlanDialogFragment; import net.osmand.plus.inapp.InAppPurchaseHelper; import net.osmand.plus.routepreparationmenu.cards.BaseCard; import net.osmand.plus.wikipedia.WikipediaDialogFragment; -import androidx.annotation.NonNull; -import androidx.cardview.widget.CardView; -import androidx.core.content.ContextCompat; - public class TroubleshootingOrPurchasingCard extends BaseCard { private static final String OSMAND_NEW_DEVICE_URL = "https://docs.osmand.net/en/main@latest/osmand/purchases#new-device--new-account"; @@ -74,6 +76,9 @@ public class TroubleshootingOrPurchasingCard extends BaseCard { } else { AndroidUtils.setBackground(mapActivity, getItButton, nightMode, R.drawable.btn_unstroked_light, R.drawable.btn_unstroked_dark); } + + ImageView getItArrow = view.findViewById(R.id.additional_button_icon); + UiUtilities.rotateImageByLayoutDirection(getItArrow); } } diff --git a/OsmAnd/src/net/osmand/plus/track/GpxBlockStatisticsBuilder.java b/OsmAnd/src/net/osmand/plus/track/GpxBlockStatisticsBuilder.java index d4bef49ecf..f3048a2867 100644 --- a/OsmAnd/src/net/osmand/plus/track/GpxBlockStatisticsBuilder.java +++ b/OsmAnd/src/net/osmand/plus/track/GpxBlockStatisticsBuilder.java @@ -45,7 +45,6 @@ import java.util.Date; import java.util.List; import static net.osmand.plus.liveupdates.LiveUpdatesFragment.getDefaultIconColorId; -import static net.osmand.plus.myplaces.GPXTabItemType.GPX_TAB_ITEM_SPEED; public class GpxBlockStatisticsBuilder { diff --git a/OsmAnd/src/net/osmand/plus/track/SplitIntervalBottomSheet.java b/OsmAnd/src/net/osmand/plus/track/SplitIntervalBottomSheet.java index 7d36e90b27..deb46ac713 100644 --- a/OsmAnd/src/net/osmand/plus/track/SplitIntervalBottomSheet.java +++ b/OsmAnd/src/net/osmand/plus/track/SplitIntervalBottomSheet.java @@ -22,12 +22,12 @@ import net.osmand.plus.R; import net.osmand.plus.UiUtilities; import net.osmand.plus.base.MenuBottomSheetDialogFragment; import net.osmand.plus.base.bottomsheetmenu.SimpleBottomSheetItem; -import net.osmand.plus.base.bottomsheetmenu.simpleitems.LongDescriptionItem; import net.osmand.plus.base.bottomsheetmenu.simpleitems.TitleItem; import net.osmand.plus.helpers.AndroidUiHelper; -import net.osmand.plus.widgets.MultiStateToggleButton; -import net.osmand.plus.widgets.MultiStateToggleButton.OnRadioItemClickListener; -import net.osmand.plus.widgets.MultiStateToggleButton.RadioItem; +import net.osmand.plus.widgets.multistatetoggle.RadioItem; +import net.osmand.plus.widgets.multistatetoggle.RadioItem.OnRadioItemClickListener; +import net.osmand.plus.widgets.multistatetoggle.TextToggleButton; +import net.osmand.plus.widgets.multistatetoggle.TextToggleButton.TextRadioItem; import org.apache.commons.logging.Log; @@ -120,11 +120,11 @@ public class SplitIntervalBottomSheet extends MenuBottomSheetDialogFragment { } private void setupTypeRadioGroup(LinearLayout buttonsContainer) { - RadioItem none = createRadioButton(GpxSplitType.NO_SPLIT, R.string.shared_string_none); - RadioItem time = createRadioButton(GpxSplitType.TIME, R.string.shared_string_time); - RadioItem distance = createRadioButton(GpxSplitType.DISTANCE, R.string.distance); + TextRadioItem none = createRadioButton(GpxSplitType.NO_SPLIT, R.string.shared_string_none); + TextRadioItem time = createRadioButton(GpxSplitType.TIME, R.string.shared_string_time); + TextRadioItem distance = createRadioButton(GpxSplitType.DISTANCE, R.string.distance); - MultiStateToggleButton radioGroup = new MultiStateToggleButton(app, buttonsContainer, nightMode); + TextToggleButton radioGroup = new TextToggleButton(app, buttonsContainer, nightMode); radioGroup.setItems(none, time, distance); if (selectedSplitType == GpxSplitType.NO_SPLIT) { @@ -134,9 +134,9 @@ public class SplitIntervalBottomSheet extends MenuBottomSheetDialogFragment { } } - private RadioItem createRadioButton(final GpxSplitType splitType, int titleId) { + private TextRadioItem createRadioButton(final GpxSplitType splitType, int titleId) { String title = app.getString(titleId); - RadioItem item = new RadioItem(title); + TextRadioItem item = new TextRadioItem(title); item.setOnClickListener(new OnRadioItemClickListener() { @Override public boolean onRadioItemClick(RadioItem radioItem, View view) { diff --git a/OsmAnd/src/net/osmand/plus/track/TrackMenuFragment.java b/OsmAnd/src/net/osmand/plus/track/TrackMenuFragment.java index 2eedf2eab5..086ffcce4f 100644 --- a/OsmAnd/src/net/osmand/plus/track/TrackMenuFragment.java +++ b/OsmAnd/src/net/osmand/plus/track/TrackMenuFragment.java @@ -1131,7 +1131,10 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card } @Override - public void openAnalyzeOnMap(GpxDisplayItem gpxItem) { + public void openAnalyzeOnMap(@NonNull GpxDisplayItem gpxItem) { + if (gpxPoint != null) { + gpxItem.locationOnMap = gpxPoint.getSelectedPoint(); + } TrackDetailsMenu trackDetailsMenu = getMapActivity().getTrackDetailsMenu(); trackDetailsMenu.setGpxItem(gpxItem); trackDetailsMenu.setSelectedGpxFile(selectedGpxFile); diff --git a/OsmAnd/src/net/osmand/plus/views/Renderable.java b/OsmAnd/src/net/osmand/plus/views/Renderable.java index dd8ba55b8a..2dbf1eb3aa 100644 --- a/OsmAnd/src/net/osmand/plus/views/Renderable.java +++ b/OsmAnd/src/net/osmand/plus/views/Renderable.java @@ -58,6 +58,7 @@ public class Renderable { public static abstract class RenderableSegment { protected static final int MIN_CULLER_ZOOM = 16; + protected static final int BORDER_TYPE_ZOOM_THRESHOLD = MapTileLayer.DEFAULT_MAX_ZOOM + MapTileLayer.OVERZOOM_IN; public List points = null; // Original list of points protected List culled = new ArrayList<>(); // Reduced/resampled list of points @@ -70,6 +71,7 @@ public class Renderable { protected Paint paint = null; // MUST be set by 'updateLocalPaint' before use protected Paint borderPaint; protected GradientScaleType scaleType = null; + protected boolean drawBorder = false; protected GpxGeometryWay geometryWay; @@ -93,12 +95,10 @@ public class Renderable { } } - public void setBorderPaint(@NonNull Paint paint) { - borderPaint = paint; - } - - public void setGradientScaleType(GradientScaleType type) { - this.scaleType = type; + public void setGradientTrackParams(GradientScaleType gradientScaleType, @NonNull Paint borderPaint, boolean shouldDrawBorder) { + this.scaleType = gradientScaleType; + this.borderPaint = borderPaint; + this.drawBorder = shouldDrawBorder; } public GpxGeometryWay getGeometryWay() { @@ -119,8 +119,10 @@ public class Renderable { updateLocalPaint(p); canvas.rotate(-tileBox.getRotate(), tileBox.getCenterPixelX(), tileBox.getCenterPixelY()); if (scaleType != null) { - drawSolid(points, borderPaint, canvas, tileBox); - drawGradient(points, paint, canvas, tileBox); + if (drawBorder && zoom < BORDER_TYPE_ZOOM_THRESHOLD) { + drawSolid(points, borderPaint, canvas, tileBox); + } + drawGradient(zoom, points, paint, canvas, tileBox); } else { drawSolid(getPointsForDrawing(), paint, canvas, tileBox); } @@ -186,8 +188,9 @@ public class Renderable { } } - protected void drawGradient(List pts, Paint p, Canvas canvas, RotatedTileBox tileBox) { + protected void drawGradient(double zoom, List pts, Paint p, Canvas canvas, RotatedTileBox tileBox) { QuadRect tileBounds = tileBox.getLatLonBounds(); + boolean drawSegmentBorder = drawBorder && zoom >= BORDER_TYPE_ZOOM_THRESHOLD; Path path = new Path(); boolean recalculateLastXY = true; WptPt lastPt = pts.get(0); @@ -196,6 +199,9 @@ public class Renderable { List gradientColors = new ArrayList<>(); float gradientAngle = 0; + List paths = new ArrayList<>(); + List gradients = new ArrayList<>(); + for (int i = 1; i < pts.size(); i++) { WptPt pt = pts.get(i); WptPt nextPt = i + 1 < pts.size() ? pts.get(i + 1) : null; @@ -209,8 +215,8 @@ public class Renderable { lastX = tileBox.getPixXFromLatLon(lastPt.lat, lastPt.lon); lastY = tileBox.getPixYFromLatLon(lastPt.lat, lastPt.lon); if (!path.isEmpty()) { - p.setShader(createGradient(gradientPoints, gradientColors)); - canvas.drawPath(path, p); + paths.add(new Path(path)); + gradients.add(createGradient(gradientPoints, gradientColors)); } path.reset(); path.moveTo(lastX, lastY); @@ -241,8 +247,21 @@ public class Renderable { lastPt = pt; } if (!path.isEmpty()) { - p.setShader(createGradient(gradientPoints, gradientColors)); - canvas.drawPath(path, p); + paths.add(new Path(path)); + gradients.add(createGradient(gradientPoints, gradientColors)); + } + + if (!paths.isEmpty()) { + if (drawSegmentBorder) { + canvas.drawPath(paths.get(0), borderPaint); + } + for (int i = 0; i < paths.size(); i++) { + if (drawSegmentBorder && i + 1 < paths.size()) { + canvas.drawPath(paths.get(i + 1), borderPaint); + } + p.setShader(gradients.get(i)); + canvas.drawPath(paths.get(i), p); + } } } diff --git a/OsmAnd/src/net/osmand/plus/views/layers/FavouritesLayer.java b/OsmAnd/src/net/osmand/plus/views/layers/FavouritesLayer.java index 656568b425..04c4ac320a 100644 --- a/OsmAnd/src/net/osmand/plus/views/layers/FavouritesLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/layers/FavouritesLayer.java @@ -106,7 +106,7 @@ public class FavouritesLayer extends OsmandMapLayer implements IContextMenuProvi List smallObjectsLatLon = new ArrayList<>(); for (FavoriteGroup group : favouritesDbHelper.getFavoriteGroups()) { List> fullObjects = new ArrayList<>(); - boolean synced = view.getApplication().getItineraryHelper().getMarkersGroup(group) != null; + boolean synced = mapMarkersHelper.getMarkersGroup(group) != null; for (FavouritePoint favoritePoint : group.getPoints()) { double lat = favoritePoint.getLatitude(); double lon = favoritePoint.getLongitude(); diff --git a/OsmAnd/src/net/osmand/plus/views/layers/GPXLayer.java b/OsmAnd/src/net/osmand/plus/views/layers/GPXLayer.java index 007e6fe700..84a1483834 100644 --- a/OsmAnd/src/net/osmand/plus/views/layers/GPXLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/layers/GPXLayer.java @@ -44,10 +44,10 @@ import net.osmand.plus.R; import net.osmand.plus.UiUtilities; import net.osmand.plus.activities.MapActivity; import net.osmand.plus.base.PointImageDrawable; -import net.osmand.plus.itinerary.ItineraryGroup; import net.osmand.plus.mapcontextmenu.controllers.SelectedGpxMenuController.SelectedGpxPoint; import net.osmand.plus.mapcontextmenu.other.TrackChartPoints; import net.osmand.plus.mapmarkers.MapMarker; +import net.osmand.plus.mapmarkers.MapMarkersGroup; import net.osmand.plus.mapmarkers.MapMarkersHelper; import net.osmand.plus.render.OsmandRenderer; import net.osmand.plus.render.OsmandRenderer.RenderingContext; @@ -83,7 +83,9 @@ import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Set; +import static net.osmand.GPXUtilities.calculateTrackBounds; import static net.osmand.plus.dialogs.ConfigureMapMenu.CURRENT_TRACK_COLOR_ATTR; import static net.osmand.plus.dialogs.ConfigureMapMenu.CURRENT_TRACK_WIDTH_ATTR; @@ -276,6 +278,7 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM } } cache.clear(); + removeCachedUnselectedTracks(selectedGPXFiles); if (!selectedGPXFiles.isEmpty()) { drawSelectedFilesSegments(canvas, tileBox, selectedGPXFiles, settings); canvas.rotate(-tileBox.getRotate(), tileBox.getCenterPixelX(), tileBox.getCenterPixelY()); @@ -455,22 +458,23 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM private void drawDirectionArrows(Canvas canvas, RotatedTileBox tileBox, List selectedGPXFiles) { if (!tileBox.isZoomAnimated()) { + QuadRect correctedQuadRect = getCorrectedQuadRect(tileBox.getLatLonBounds()); for (SelectedGpxFile selectedGpxFile : selectedGPXFiles) { boolean showArrows = isShowArrowsForTrack(selectedGpxFile.getGpxFile()); - if (showArrows) { - QuadRect correctedQuadRect = getCorrectedQuadRect(tileBox.getLatLonBounds()); - String width = getTrackWidthName(selectedGpxFile.getGpxFile(), defaultTrackWidthPref.get()); - float trackWidth = getTrackWidth(width, defaultTrackWidth); - int trackColor = getTrackColor(selectedGpxFile.getGpxFile(), cachedColor); - int arrowColor = UiUtilities.getContrastColor(view.getApplication(), trackColor, false); - GradientScaleType scaleType = getGradientScaleType(selectedGpxFile.getGpxFile()); - List segments = scaleType != null ? - getCachedSegments(selectedGpxFile, scaleType) : selectedGpxFile.getPointsToDisplay(); - for (TrkSegment segment : segments) { - if (segment.renderer instanceof Renderable.RenderableSegment) { - ((Renderable.RenderableSegment) segment.renderer) - .drawGeometry(canvas, tileBox, correctedQuadRect, arrowColor, trackColor, trackWidth); - } + if (!showArrows || !QuadRect.trivialOverlap(correctedQuadRect, calculateTrackBounds(selectedGpxFile.getPointsToDisplay()))) { + continue; + } + String width = getTrackWidthName(selectedGpxFile.getGpxFile(), defaultTrackWidthPref.get()); + float trackWidth = getTrackWidth(width, defaultTrackWidth); + int trackColor = getTrackColor(selectedGpxFile.getGpxFile(), cachedColor); + int arrowColor = UiUtilities.getContrastColor(view.getApplication(), trackColor, false); + GradientScaleType scaleType = getGradientScaleType(selectedGpxFile.getGpxFile()); + List segments = scaleType != null ? + getCachedSegments(selectedGpxFile, scaleType) : selectedGpxFile.getPointsToDisplay(); + for (TrkSegment segment : segments) { + if (segment.renderer instanceof Renderable.RenderableSegment) { + ((Renderable.RenderableSegment) segment.renderer) + .drawGeometry(canvas, tileBox, correctedQuadRect, arrowColor, trackColor, trackWidth); } } } @@ -539,7 +543,7 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM for (SelectedGpxFile g : selectedGPXFiles) { List> fullObjects = new ArrayList<>(); int fileColor = getFileColor(g); - boolean synced = view.getApplication().getItineraryHelper().getMarkersGroup(g.getGpxFile()) != null; + boolean synced = mapMarkersHelper.getMarkersGroup(g.getGpxFile()) != null; for (WptPt wpt : getListStarPoints(g)) { if (wpt.lat >= latLonBounds.bottom && wpt.lat <= latLonBounds.top && wpt.lon >= latLonBounds.left && wpt.lon <= latLonBounds.right @@ -686,17 +690,18 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM } } - private void drawSelectedFileSegments(SelectedGpxFile selectedGpxFile, boolean currentTrack, Canvas canvas, - RotatedTileBox tileBox, DrawSettings settings) { - boolean visible = QuadRect.trivialOverlap(tileBox.getLatLonBounds(), GPXUtilities.calculateTrackBounds(selectedGpxFile.getPointsToDisplay())); - if (!selectedGpxFile.getGpxFile().hasTrkPt() || !visible) { + private void drawSelectedFileSegments(SelectedGpxFile selectedGpxFile, boolean currentTrack, + Canvas canvas, RotatedTileBox tileBox, DrawSettings settings) { + GPXFile gpxFile = selectedGpxFile.getGpxFile(); + GradientScaleType scaleType = getGradientScaleType(gpxFile); + + boolean visible = QuadRect.trivialOverlap(tileBox.getLatLonBounds(), calculateTrackBounds(selectedGpxFile.getPointsToDisplay())); + if (!gpxFile.hasTrkPt() && scaleType != null || !visible) { + segmentsCache.remove(selectedGpxFile.getGpxFile().path); return; } - GPXFile gpxFile = selectedGpxFile.getGpxFile(); - GradientScaleType scaleType = getGradientScaleType(gpxFile); List segments = new ArrayList<>(); - if (scaleType == null) { segments.addAll(selectedGpxFile.getPointsToDisplay()); } else { @@ -719,8 +724,7 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM updatePaints(color, width, selectedGpxFile.isRoutePoints(), currentTrack, settings, tileBox); if (ts.renderer instanceof Renderable.RenderableSegment) { Renderable.RenderableSegment renderableSegment = (Renderable.RenderableSegment) ts.renderer; - renderableSegment.setBorderPaint(borderPaint); - renderableSegment.setGradientScaleType(scaleType); + renderableSegment.setGradientTrackParams(scaleType, borderPaint, true); renderableSegment.drawSegment(view.getZoom(), paint, canvas, tileBox); } } @@ -729,13 +733,12 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM private List getCachedSegments(SelectedGpxFile selectedGpxFile, GradientScaleType scaleType) { GPXFile gpxFile = selectedGpxFile.getGpxFile(); String path = gpxFile.path; - long modifiedTime = gpxFile.modifiedTime; CachedTrack cachedTrack = segmentsCache.get(path); if (cachedTrack == null) { - cachedTrack = new CachedTrack(view.getApplication(), modifiedTime); + cachedTrack = new CachedTrack(view.getApplication(), selectedGpxFile); segmentsCache.put(path, cachedTrack); } - return cachedTrack.getCachedSegments(selectedGpxFile, view.getZoom(), scaleType, getColorizationPalette(gpxFile, scaleType)); + return cachedTrack.getCachedSegments(view.getZoom(), scaleType, getColorizationPalette(gpxFile, scaleType)); } private float getTrackWidth(String width, float defaultTrackWidth) { @@ -1051,6 +1054,19 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM return name.replace('_', ' '); } + private void removeCachedUnselectedTracks(List selectedGpxFiles) { + Set cachedTracksPaths = segmentsCache.keySet(); + List selectedTracksPaths = new ArrayList<>(); + for (SelectedGpxFile gpx : selectedGpxFiles) { + selectedTracksPaths.add(gpx.getGpxFile().path); + } + for (Iterator iterator = cachedTracksPaths.iterator(); iterator.hasNext(); ) { + if (!selectedTracksPaths.contains(iterator.next())) { + iterator.remove(); + } + } + } + @Override public boolean disableSingleTap() { return isInTrackAppearanceMode(); @@ -1210,9 +1226,9 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM } private void syncGpx(GPXFile gpxFile) { - ItineraryGroup group = view.getApplication().getItineraryHelper().getMarkersGroup(gpxFile); + MapMarkersGroup group = view.getApplication().getMapMarkersHelper().getMarkersGroup(gpxFile); if (group != null) { - view.getApplication().getItineraryHelper().runSynchronization(group); + mapMarkersHelper.runSynchronization(group); } } @@ -1220,20 +1236,21 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM private OsmandApplication app; - private long modifiedTime; + private final SelectedGpxFile selectedGpxFile; private final Map> cache = new HashMap<>(); - public CachedTrack(@NonNull OsmandApplication app, long modifiedTime) { + private long prevModifiedTime = -1; + + public CachedTrack(@NonNull OsmandApplication app, @NonNull SelectedGpxFile selectedGpxFile) { this.app = app; - this.modifiedTime = modifiedTime; + this.selectedGpxFile = selectedGpxFile; } - public List getCachedSegments(@NonNull SelectedGpxFile selectedGpxFile, int zoom, - @NonNull GradientScaleType scaleType, + public List getCachedSegments(int zoom, @NonNull GradientScaleType scaleType, int[] gradientPalette) { GPXFile gpxFile = selectedGpxFile.getGpxFile(); String trackId = zoom + "_" + scaleType.toString(); - if (modifiedTime == gpxFile.modifiedTime) { + if (prevModifiedTime == gpxFile.modifiedTime) { List segments = cache.get(trackId); if (segments == null) { segments = calculateGradientTrack(selectedGpxFile, zoom, scaleType, gradientPalette); @@ -1242,7 +1259,7 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM return segments; } else { cache.clear(); - modifiedTime = gpxFile.modifiedTime; + prevModifiedTime = gpxFile.modifiedTime; List segments = calculateGradientTrack(selectedGpxFile, zoom, scaleType, gradientPalette); cache.put(trackId, segments); return segments; @@ -1255,7 +1272,7 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM RouteColorize colorize = new RouteColorize(zoom, gpxFile, selectedGpxFile.getTrackAnalysis(app), scaleType.toColorizationType(), app.getSettings().getApplicationMode().getMaxSpeed()); colorize.setPalette(gradientPalette); - List colorsOfPoints = colorize.getResult(true); + List colorsOfPoints = colorize.getResult(true); return createSimplifiedSegments(selectedGpxFile.getGpxFile(), colorsOfPoints, scaleType); } diff --git a/OsmAnd/src/net/osmand/plus/views/layers/POIMapLayer.java b/OsmAnd/src/net/osmand/plus/views/layers/POIMapLayer.java index 1adeed7671..49e5d49a56 100644 --- a/OsmAnd/src/net/osmand/plus/views/layers/POIMapLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/layers/POIMapLayer.java @@ -357,19 +357,7 @@ public class POIMapLayer extends OsmandMapLayer implements ContextMenuLayer.ICon @Override public PointDescription getObjectName(Object o) { if (o instanceof Amenity) { - Amenity amenity = (Amenity) o; - String preferredLang = app.getSettings().MAP_PREFERRED_LOCALE.get(); - boolean transliterateNames = app.getSettings().MAP_TRANSLITERATE_NAMES.get(); - - if (amenity.getType().isWiki()) { - if (Algorithms.isEmpty(preferredLang)) { - preferredLang = app.getLanguage(); - } - preferredLang = OsmandPlugin.onGetMapObjectsLocale(amenity, preferredLang); - } - - return new PointDescription(PointDescription.POINT_TYPE_POI, - amenity.getName(preferredLang, transliterateNames)); + return new PointDescription(PointDescription.POINT_TYPE_POI, getAmenityName((Amenity) o)); } return null; } @@ -431,8 +419,20 @@ public class POIMapLayer extends OsmandMapLayer implements ContextMenuLayer.ICon @Override public String getText(Amenity o) { - return o.getName(view.getSettings().MAP_PREFERRED_LOCALE.get(), - view.getSettings().MAP_TRANSLITERATE_NAMES.get()); + return getAmenityName(o); + } + + private String getAmenityName(Amenity amenity) { + String locale = app.getSettings().MAP_PREFERRED_LOCALE.get(); + + if (amenity.getType().isWiki()) { + if (Algorithms.isEmpty(locale)) { + locale = app.getLanguage(); + } + locale = OsmandPlugin.onGetMapObjectsLocale(amenity, locale); + } + + return amenity.getName(locale, app.getSettings().MAP_TRANSLITERATE_NAMES.get()); } @Override diff --git a/OsmAnd/src/net/osmand/plus/widgets/multistatetoggle/IconToggleButton.java b/OsmAnd/src/net/osmand/plus/widgets/multistatetoggle/IconToggleButton.java new file mode 100644 index 0000000000..3862ffae77 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/widgets/multistatetoggle/IconToggleButton.java @@ -0,0 +1,70 @@ +package net.osmand.plus.widgets.multistatetoggle; + +import android.graphics.drawable.Drawable; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.LinearLayout; + +import androidx.annotation.ColorInt; +import androidx.annotation.NonNull; + +import net.osmand.plus.OsmandApplication; +import net.osmand.plus.R; +import net.osmand.plus.widgets.multistatetoggle.IconToggleButton.IconRadioItem; + +public class IconToggleButton extends MultiStateToggleButton { + + public IconToggleButton(@NonNull OsmandApplication app, + @NonNull LinearLayout container, + boolean nightMode) { + super(app, container, nightMode); + } + + @Override + protected int getRadioItemLayoutId() { + return R.layout.custom_radio_btn_icon_item; + } + + @Override + protected void initItemView(@NonNull ViewGroup view, + @NonNull IconRadioItem item) { + if (item.isUseDefaultColor()) { + ImageView ivIcon = view.findViewById(R.id.icon); + ivIcon.setImageDrawable(uiUtilities.getIcon(item.getIconId())); + } + } + + @Override + protected void updateItemView(@NonNull ViewGroup view, + @NonNull IconRadioItem item, + @ColorInt int color) { + if (!item.isUseDefaultColor()) { + ImageView ivIcon = view.findViewById(R.id.icon); + Drawable icon = uiUtilities.getPaintedIcon(item.getIconId(), color); + ivIcon.setImageDrawable(icon); + } + } + + public static class IconRadioItem extends RadioItem { + + private final int iconId; + private final boolean useDefaultColor; + + public IconRadioItem(int iconId) { + this(iconId, false); + } + + public IconRadioItem(int iconId, boolean useDefaultColor) { + this.iconId = iconId; + this.useDefaultColor = useDefaultColor; + } + + public int getIconId() { + return iconId; + } + + public boolean isUseDefaultColor() { + return useDefaultColor; + } + } +} diff --git a/OsmAnd/src/net/osmand/plus/widgets/MultiStateToggleButton.java b/OsmAnd/src/net/osmand/plus/widgets/multistatetoggle/MultiStateToggleButton.java similarity index 68% rename from OsmAnd/src/net/osmand/plus/widgets/MultiStateToggleButton.java rename to OsmAnd/src/net/osmand/plus/widgets/multistatetoggle/MultiStateToggleButton.java index 605584f5b8..f3d2e5aafb 100644 --- a/OsmAnd/src/net/osmand/plus/widgets/MultiStateToggleButton.java +++ b/OsmAnd/src/net/osmand/plus/widgets/multistatetoggle/MultiStateToggleButton.java @@ -1,4 +1,4 @@ -package net.osmand.plus.widgets; +package net.osmand.plus.widgets.multistatetoggle; import android.graphics.Color; import android.graphics.drawable.GradientDrawable; @@ -6,38 +6,58 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.LinearLayout; -import android.widget.TextView; +import androidx.annotation.ColorInt; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.core.content.ContextCompat; import net.osmand.AndroidUtils; import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; import net.osmand.plus.UiUtilities; +import net.osmand.plus.widgets.multistatetoggle.RadioItem.OnRadioItemClickListener; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; +import java.util.Collection; import java.util.List; -public class MultiStateToggleButton { +public abstract class MultiStateToggleButton<_Radio extends RadioItem> { - private List items = new ArrayList<>(); - private OsmandApplication app; - private List buttons = new ArrayList<>(); - private List dividers = new ArrayList<>(); - private RadioItem selectedItem; + protected final OsmandApplication app; + protected final UiUtilities uiUtilities; - private LinearLayout container; - private boolean nightMode; + private final LinearLayout container; + private final List buttons = new ArrayList<>(); + private final List dividers = new ArrayList<>(); - public MultiStateToggleButton(OsmandApplication app, LinearLayout container, boolean nightMode) { + protected final List<_Radio> items = new ArrayList<>(); + protected final boolean nightMode; + protected boolean isEnabled; + protected RadioItem selectedItem; + + public MultiStateToggleButton(@NonNull OsmandApplication app, + @NonNull LinearLayout container, + boolean nightMode) { this.app = app; + this.uiUtilities = app.getUIUtilities(); this.container = container; this.nightMode = nightMode; } - public void setItems(RadioItem firstBtn, RadioItem secondBtn, RadioItem... other) { + public void setItems(Collection<_Radio> radioItems) { + if (radioItems == null || radioItems.size() < 2) return; + items.clear(); + items.addAll(radioItems); + initView(); + } + + @SafeVarargs + public final void setItems(@NonNull _Radio firstBtn, + @NonNull _Radio secondBtn, + @Nullable _Radio... other) { items.clear(); items.add(firstBtn); items.add(secondBtn); @@ -47,6 +67,11 @@ public class MultiStateToggleButton { initView(); } + public final void setSelectedItem(@Nullable RadioItem selectedItem) { + this.selectedItem = selectedItem; + updateView(); + } + private void initView() { buttons.clear(); dividers.clear(); @@ -60,16 +85,10 @@ public class MultiStateToggleButton { updateView(); } - private boolean isLastItem(int index) { - return index == items.size() - 1; - } - - private void createBtn(@NonNull final RadioItem item) { + private void createBtn(@NonNull final _Radio item) { LayoutInflater inflater = UiUtilities.getInflater(app, nightMode); ViewGroup button = (ViewGroup) inflater.inflate( - R.layout.custom_radio_btn_text_item, container, false); - TextView title = button.findViewById(R.id.title); - title.setText(item.getTitle()); + getRadioItemLayoutId(), container, false); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -79,6 +98,7 @@ public class MultiStateToggleButton { } } }); + initItemView(button, item); buttons.add(button); container.addView(button); } @@ -95,11 +115,6 @@ public class MultiStateToggleButton { container.addView(divider); } - public void setSelectedItem(RadioItem selectedItem) { - this.selectedItem = selectedItem; - updateView(); - } - private void updateView() { updateView(true); } @@ -123,15 +138,14 @@ public class MultiStateToggleButton { showAllDividers(); for (int i = 0; i < items.size(); i++) { - RadioItem item = items.get(i); + _Radio item = items.get(i); ViewGroup container = buttons.get(i); container.setEnabled(isEnabled); - TextView tvTitle = (TextView) container.findViewById(R.id.title); if (selectedItem == item) { if (i == 0) { background.setCornerRadii(isLayoutRtl ? rightBtnRadii : leftBtnRadii); hideDividers(0); - } else if (i == items.size() - 1) { + } else if (isLastItem(i)) { background.setCornerRadii(isLayoutRtl ? leftBtnRadii : rightBtnRadii); hideDividers(dividers.size() - 1); } else { @@ -139,14 +153,21 @@ public class MultiStateToggleButton { hideDividers(i - 1, i); } container.setBackgroundDrawable(background); - tvTitle.setTextColor(textColor); + updateItemView(container, item, textColor); } else { container.setBackgroundColor(Color.TRANSPARENT); - tvTitle.setTextColor(activeColor); + updateItemView(container, item, activeColor); } } } + protected abstract int getRadioItemLayoutId(); + + protected abstract void initItemView(@NonNull ViewGroup view, @NonNull _Radio item); + + protected abstract void updateItemView(@NonNull ViewGroup view, @NonNull _Radio item, + @ColorInt int color); + private void showAllDividers() { for (View divider : dividers) { divider.setVisibility(View.VISIBLE); @@ -161,28 +182,7 @@ public class MultiStateToggleButton { } } - public static class RadioItem { - private String title; - private OnRadioItemClickListener listener; - - public RadioItem(String title) { - this.title = title; - } - - public void setOnClickListener(OnRadioItemClickListener listener) { - this.listener = listener; - } - - public String getTitle() { - return title; - } - - public OnRadioItemClickListener getListener() { - return listener; - } - } - - public interface OnRadioItemClickListener { - boolean onRadioItemClick(RadioItem radioItem, View view); + private boolean isLastItem(int index) { + return index == items.size() - 1; } } diff --git a/OsmAnd/src/net/osmand/plus/widgets/multistatetoggle/RadioItem.java b/OsmAnd/src/net/osmand/plus/widgets/multistatetoggle/RadioItem.java new file mode 100644 index 0000000000..c23f6a925f --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/widgets/multistatetoggle/RadioItem.java @@ -0,0 +1,20 @@ +package net.osmand.plus.widgets.multistatetoggle; + +import android.view.View; + +public class RadioItem { + + private OnRadioItemClickListener listener; + + public void setOnClickListener(OnRadioItemClickListener listener) { + this.listener = listener; + } + + public OnRadioItemClickListener getListener() { + return listener; + } + + public interface OnRadioItemClickListener { + boolean onRadioItemClick(RadioItem radioItem, View view); + } +} diff --git a/OsmAnd/src/net/osmand/plus/widgets/multistatetoggle/TextToggleButton.java b/OsmAnd/src/net/osmand/plus/widgets/multistatetoggle/TextToggleButton.java new file mode 100644 index 0000000000..048ebca622 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/widgets/multistatetoggle/TextToggleButton.java @@ -0,0 +1,54 @@ +package net.osmand.plus.widgets.multistatetoggle; + +import android.view.ViewGroup; +import android.widget.LinearLayout; +import android.widget.TextView; + +import androidx.annotation.ColorInt; +import androidx.annotation.NonNull; + +import net.osmand.plus.OsmandApplication; +import net.osmand.plus.R; +import net.osmand.plus.widgets.multistatetoggle.TextToggleButton.TextRadioItem; + +public class TextToggleButton extends MultiStateToggleButton { + + public TextToggleButton(@NonNull OsmandApplication app, + @NonNull LinearLayout container, + boolean nightMode) { + super(app, container, nightMode); + } + + @Override + protected int getRadioItemLayoutId() { + return R.layout.custom_radio_btn_text_item; + } + + @Override + protected void initItemView(@NonNull ViewGroup view, @NonNull TextRadioItem item) { + TextView title = view.findViewById(R.id.title); + title.setText(item.getTitle()); + } + + @Override + protected void updateItemView(@NonNull ViewGroup view, + @NonNull TextRadioItem item, + @ColorInt int color) { + TextView tvTitle = (TextView) view.findViewById(R.id.title); + tvTitle.setTextColor(color); + } + + public static class TextRadioItem extends RadioItem { + + private final String title; + + public TextRadioItem(String title) { + this.title = title; + } + + public String getTitle() { + return title; + } + } + +} diff --git a/OsmAnd/src/net/osmand/plus/wikipedia/WikiArticleHelper.java b/OsmAnd/src/net/osmand/plus/wikipedia/WikiArticleHelper.java index 02f31efe8e..b21ca8527b 100644 --- a/OsmAnd/src/net/osmand/plus/wikipedia/WikiArticleHelper.java +++ b/OsmAnd/src/net/osmand/plus/wikipedia/WikiArticleHelper.java @@ -142,7 +142,7 @@ public class WikiArticleHelper { public boolean publish(Amenity amenity) { List allNames = amenity.getAllNames(false); for (String amenityName : allNames) { - if (name.equalsIgnoreCase(amenityName)) { + if (name.equals(amenityName)) { results.add(amenity); break; } diff --git a/OsmAnd/src/net/osmand/plus/wikivoyage/data/TravelObfHelper.java b/OsmAnd/src/net/osmand/plus/wikivoyage/data/TravelObfHelper.java index 1cde05050b..7e8a0a3f02 100644 --- a/OsmAnd/src/net/osmand/plus/wikivoyage/data/TravelObfHelper.java +++ b/OsmAnd/src/net/osmand/plus/wikivoyage/data/TravelObfHelper.java @@ -72,7 +72,7 @@ public class TravelObfHelper implements TravelHelper { public static final String ROUTE_TRACK = "route_track"; public static final int ARTICLE_SEARCH_RADIUS = 50 * 1000; public static final int SAVED_ARTICLE_SEARCH_RADIUS = 30 * 1000; - public static final int MAX_SEARCH_RADIUS = 10000 * 1000; + public static final int MAX_SEARCH_RADIUS = 800 * 1000; public static final String REF_TAG = "ref"; public static final String NAME_TAG = "name"; @@ -124,8 +124,8 @@ public class TravelObfHelper implements TravelHelper { final LatLon location = app.getMapViewTrackingUtilities().getMapLocation(); for (final BinaryMapIndexReader reader : getReaders()) { try { - searchAmenity(foundAmenities, location, reader, searchRadius, -1, ROUTE_ARTICLE); - searchAmenity(foundAmenities, location, reader, searchRadius / 5, 15, ROUTE_TRACK); + searchAmenity(foundAmenities, location, reader, searchRadius, -1, ROUTE_ARTICLE, lang); + searchAmenity(foundAmenities, location, reader, searchRadius / 5, 15, ROUTE_TRACK, null); } catch (Exception e) { LOG.error(e.getMessage(), e); } @@ -172,12 +172,14 @@ public class TravelObfHelper implements TravelHelper { private void searchAmenity(final List> amenitiesList, LatLon location, final BinaryMapIndexReader reader, int searchRadius, int zoom, - String searchFilter) throws IOException { + String searchFilter, final String lang) throws IOException { reader.searchPoi(BinaryMapIndexReader.buildSearchPoiRequest( location, searchRadius, zoom, getSearchFilter(searchFilter), new ResultMatcher() { @Override public boolean publish(Amenity object) { - amenitiesList.add(new Pair<>(reader.getFile(), object)); + if (lang == null || object.getNamesMap(true).containsKey(lang)) { + amenitiesList.add(new Pair<>(reader.getFile(), object)); + } return false; } @@ -231,7 +233,7 @@ public class TravelObfHelper implements TravelHelper { } res.user = Algorithms.emptyIfNull(amenity.getTagContent(USER)); res.activityType = Algorithms.emptyIfNull(amenity.getTagContent(ACTIVITY_TYPE)); - articles.put("en", res); + articles.put("", res); return articles; } @@ -1083,4 +1085,4 @@ public class TravelObfHelper implements TravelHelper { } } } -} \ No newline at end of file +}