Compare commits

...

57 commits

Author SHA1 Message Date
max-klaus
9d39e2f805 Fix backup error handling 2021-04-23 20:40:06 +03:00
Dmitry
3ce4082997 Added new icons for Restore and Read from file 2021-04-23 18:18:57 +03:00
Dmitry
f528ae4b43 Added new icons for cellular data control, upload/dowload to cloud 2021-04-23 18:14:08 +03:00
Vitaliy
ed0f6d6882
Merge pull request #11438 from osmandapp/route_altitude_slope
Add gradient coloring of route line
2021-04-23 00:48:48 +03:00
Hosted Weblate
f0dc959bdc
Merge branch 'origin/master' into Weblate. 2021-04-22 23:32:05 +02:00
Sérgio Morais
aa2c111e88
Translated using Weblate (Portuguese)
Currently translated at 100.0% (3927 of 3927 strings)
2021-04-22 23:32:02 +02:00
Eduardo Addad de Oliveira
74efa9f2b8
Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (3927 of 3927 strings)
2021-04-22 23:32:02 +02:00
Eduardo Addad de Oliveira
92d59f6521
Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (3721 of 3721 strings)
2021-04-22 23:32:01 +02:00
Zmicer Turok
d1fb9d5129
Translated using Weblate (Belarusian)
Currently translated at 98.0% (3648 of 3721 strings)
2021-04-22 23:32:00 +02:00
ace shadow
22237493a6
Translated using Weblate (Slovak)
Currently translated at 100.0% (3721 of 3721 strings)
2021-04-22 23:32:00 +02:00
Evgenii Martynenko
3754cf7822
Translated using Weblate (Russian)
Currently translated at 100.0% (3721 of 3721 strings)
2021-04-22 23:31:59 +02:00
Hinagiku Zeppeki
2437fb722a
Translated using Weblate (Japanese)
Currently translated at 98.4% (3663 of 3721 strings)
2021-04-22 23:31:58 +02:00
Sérgio Morais
8accf25b60
Translated using Weblate (Portuguese)
Currently translated at 100.0% (3721 of 3721 strings)
2021-04-22 23:31:58 +02:00
max-klaus
974ad80734 Fix backup sync and progress 2021-04-22 20:35:27 +03:00
Vitaliy
500cb007b8 Merge branch 'master' into route_altitude_slope
# Conflicts:
#	OsmAnd/src/net/osmand/plus/routing/cards/RouteLineColorCard.java
2021-04-22 19:34:46 +03:00
Vitaliy
e6be5dcab5
Merge pull request #11502 from osmandapp/Fix_11423
Fix #11423 Wikipedia labels on the map shown on few languages
2021-04-22 18:50:59 +03:00
nazar-kutz
e2d659e859 Fix #11423 Wikipedia labels on the map shown on few languages 2021-04-22 18:43:37 +03:00
max-klaus
603f32df63 Fix test backup screen 2021-04-22 16:54:12 +03:00
Vitaliy
c1b416448f
Merge pull request #11500 from osmandapp/FixRtl
RTL
2021-04-22 15:17:56 +03:00
nazar-kutz
42604ac3b6 RTL: fix slider on Announcement Time dialog 2021-04-22 15:02:10 +03:00
nazar-kutz
1ac919b8d3 RTL: fix Live Updates screen 2021-04-22 14:12:49 +03:00
cepprice
4de1aa9193 Fix crash and other fixes 2021-04-22 16:04:56 +05:00
Vitaliy
fe9a5c43bd
Merge pull request #11499 from osmandapp/fix_transport_exit
Fix #11362 name of underground station
2021-04-22 13:32:03 +03:00
Dima-1
32dff8b9e2 Fix #11362 name of underground station 2021-04-22 13:02:16 +03:00
vshcherb
72ca4055fc
Update OsmandSettings.java 2021-04-22 10:04:09 +02:00
vshcherb
8950a05446
Merge pull request #11452 from stalker314314/add-serbian-latin
Adding Serbian Latin translation for phrases and Telegram
2021-04-21 16:24:59 +02:00
vshcherb
60b72f53be
Merge pull request #11487 from osmandapp/markers_refactor
Revert itinerary and refactor group markers
2021-04-21 14:55:36 +02:00
Victor Shcherb
560c229277 Refactor group markers 2021-04-21 15:11:55 +03:00
Victor Shcherb
c40eb96bd7 Remove CategoriesSubHeader 2021-04-21 15:11:51 +03:00
Vitaliy
7f795a4e43 Fix compilation 2021-04-21 15:11:31 +03:00
Vitaliy
eefe1a00ac Revert "Create ItineraryHelper"
This reverts commit 7218e7d4
2021-04-21 15:07:18 +03:00
Vitaliy
cb18d45e43 Revert "Move markerGroups to ItineraryHelper"
This reverts commit a40f802a
2021-04-21 15:05:31 +03:00
Hosted Weblate
d2fe5b7e3f
Merge branch 'origin/master' into Weblate. 2021-04-21 13:15:24 +02:00
Babos Gábor
5670680d1e
Translated using Weblate (Hungarian)
Currently translated at 100.0% (271 of 271 strings)

Translation: OsmAnd/Telegram
Translate-URL: https://hosted.weblate.org/projects/osmand/telegram/hu/
2021-04-21 13:15:24 +02:00
Jeff Huang
dd1195b060
Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (3721 of 3721 strings)
2021-04-21 13:15:23 +02:00
Sveinn í Felli
d0d60e9d2e
Translated using Weblate (Icelandic)
Currently translated at 100.0% (3927 of 3927 strings)
2021-04-21 13:15:13 +02:00
Sveinn í Felli
0d26b83435
Translated using Weblate (Icelandic)
Currently translated at 100.0% (3721 of 3721 strings)
2021-04-21 13:15:13 +02:00
Verdulo
83885c373c
Translated using Weblate (Esperanto)
Currently translated at 100.0% (3721 of 3721 strings)
2021-04-21 13:15:12 +02:00
Ldm Public
3ff1d1a39a
Translated using Weblate (French)
Currently translated at 100.0% (3927 of 3927 strings)
2021-04-21 13:15:11 +02:00
Softmap
5f9e931482
Translated using Weblate (Arabic)
Currently translated at 100.0% (3721 of 3721 strings)
2021-04-21 13:15:09 +02:00
Yaron Shahrabani
b1d1007cb6
Translated using Weblate (Hebrew)
Currently translated at 99.9% (3719 of 3721 strings)
2021-04-21 13:15:08 +02:00
Ihor Hordiichuk
6948315605
Translated using Weblate (Ukrainian)
Currently translated at 100.0% (3721 of 3721 strings)
2021-04-21 13:15:08 +02:00
Athoss
81687551e7
Translated using Weblate (Hungarian)
Currently translated at 99.9% (3720 of 3721 strings)
2021-04-21 13:15:07 +02:00
Oğuz Ersen
da89e737ec
Translated using Weblate (Turkish)
Currently translated at 100.0% (3721 of 3721 strings)
2021-04-21 13:15:07 +02:00
Oliver
8c992bb4a9
Translated using Weblate (German)
Currently translated at 99.9% (3720 of 3721 strings)
2021-04-21 13:15:06 +02:00
Ldm Public
0ca18e6d5f
Translated using Weblate (French)
Currently translated at 99.9% (3720 of 3721 strings)
2021-04-21 13:15:05 +02:00
Sérgio Morais
d9de7ba4b0
Translated using Weblate (Portuguese)
Currently translated at 100.0% (3721 of 3721 strings)
2021-04-21 13:15:04 +02:00
Babos Gábor
b5a27d99b8
Translated using Weblate (Hungarian)
Currently translated at 99.9% (3720 of 3721 strings)
2021-04-21 13:14:57 +02:00
Vitaliy
a5f0fa9aac
Merge pull request #11485 from osmandapp/Split-interval-title
"Split interval:" not translated
2021-04-21 14:14:12 +03:00
androiddevkotlin
43564125ce "Split interval:" not translated
https://github.com/osmandapp/OsmAnd/issues/11444
2021-04-21 13:57:53 +03:00
cepprice
13b6b74e13 Fix border type switching 2021-04-18 14:43:07 +05:00
Branko Kokanovic
c71253a8fd Adding Serbian Latin translation for phrases and Telegram
It is based on Serbian, but it is transliterated (so, easily obtained by
automation). While it was easy to produce and Serbian is 100%, I decided
to upload it in same commit.
2021-04-18 00:52:32 +02:00
cepprice
fbec2abd19 Small fixes 2021-04-16 20:23:49 +05:00
cepprice
0bdf2dfa8a Add UI to configure route line 2021-04-16 17:51:01 +05:00
cepprice
aa6b04a924 Fix spoiled points 2021-04-16 11:02:27 +05:00
cepprice
149b89090b Add configurable drawing of route line border 2021-04-15 17:28:48 +05:00
cepprice
ff7aa932a3 Add gradient route line 2021-04-15 14:10:41 +05:00
112 changed files with 6255 additions and 1218 deletions

View file

@ -186,7 +186,7 @@ public class RouteColorize {
public List<RouteColorizationPoint> getResult(boolean simplify) {
List<RouteColorizationPoint> result = new ArrayList<>();
if (simplify) {
result = simplify();
result = simplify(zoom);
} else {
for (int i = 0; i < latitudes.length; i++) {
result.add(new RouteColorizationPoint(i, latitudes[i], longitudes[i], values[i]));
@ -200,7 +200,7 @@ public class RouteColorize {
public int getColorByValue(double value) {
if (Double.isNaN(value)) {
value = (minValue + maxValue) / 2;
value = colorizationType == ColorizationType.SLOPE ? minValue : (minValue + maxValue) / 2;
}
for (int i = 0; i < palette.length - 1; i++) {
if (value == palette[i][VALUE_INDEX])
@ -242,7 +242,7 @@ public class RouteColorize {
return rgbaToDecimal(0, 0, 0, 0);
}
private List<RouteColorizationPoint> simplify() {
public List<RouteColorizationPoint> simplify(int zoom) {
if (dataList == null) {
dataList = new ArrayList<>();
for (int i = 0; i < latitudes.length; i++) {
@ -266,6 +266,8 @@ public class RouteColorize {
List<RouteColorizationPoint> sublist = dataList.subList(prevId, currentId);
simplified.addAll(getExtremums(sublist));
}
Node lastSurvivedPoint = result.get(result.size() - 1);
simplified.add(dataList.get((int) lastSurvivedPoint.getId()));
return simplified;
}

View file

@ -0,0 +1,274 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="shared_string_select">Označi</string>
<string name="shared_string_enable">Omogući</string>
<string name="altitude">Nadmorska visina</string>
<string name="shared_string_search">Traži</string>
<string name="shared_string_ok">U redu</string>
<string name="shared_string_update">Ažuriraj</string>
<string name="average_altitude">Prosečna visina</string>
<string name="average_speed">Prosečna brzina</string>
<string name="shared_string_map">Karta</string>
<string name="shared_string_add">Dodaj</string>
<string name="shared_string_hide">Sakrij</string>
<string name="shared_string_status">Stanje</string>
<string name="shared_string_disable">Onemogući</string>
<string name="shared_string_save">Sačuvaj</string>
<string name="shared_string_name">Ime</string>
<string name="shared_string_sort">Sortiraj</string>
<string name="shared_string_exit">Izlaz</string>
<string name="shared_string_close">Zatvori</string>
<string name="shared_string_all">Sve</string>
<string name="shared_string_off">Isključeno</string>
<string name="shared_string_install">Instaliraj</string>
<string name="shared_string_share">Deli</string>
<string name="shared_string_back">Nazad</string>
<string name="shared_string_continue">Nastavi</string>
<string name="shared_string_cancel">Otkaži</string>
<string name="shared_string_settings">Postavke</string>
<string name="osmand_service">Pozadinski režim</string>
<string name="yard">yd</string>
<string name="foot">ft</string>
<string name="mile">mi</string>
<string name="km">km</string>
<string name="m">m</string>
<string name="nm">nmi</string>
<string name="min_mile">min/m</string>
<string name="min_km">min/km</string>
<string name="m_s">m/s</string>
<string name="km_h">km/h</string>
<string name="mile_per_hour">mph</string>
<string name="si_kmh">Kilometara na sat</string>
<string name="si_mph">Milja na sat</string>
<string name="si_m_s">Metara u sekundi</string>
<string name="si_min_km">Minuta po kilometru</string>
<string name="si_min_m">Minuta po milji</string>
<string name="si_mi_feet">Milje/stope</string>
<string name="si_mi_yard">Milje/jardi</string>
<string name="si_km_m">Kilometri/metri</string>
<string name="si_nm">Nautičke milje</string>
<string name="si_mi_meters">Milje/metri</string>
<string name="shared_string_apply">Primeni</string>
<string name="shared_string_enabled">Uključen</string>
<string name="units_and_formats">Merne jedinice &amp; formatiranja</string>
<string name="unit_of_length_descr">Promeni jedinice za dužinu.</string>
<string name="unit_of_length">Jedinice dužine</string>
<string name="shared_string_appearance">Izgled</string>
<string name="timeline">Vremenska linija</string>
<string name="live_now">Uživo sada</string>
<string name="my_location">Moja lokacija</string>
<string name="welcome_descr"><b>OsmAnd Pratioc</b> omogućava vam da delite svoju lokaciju i vidite lokaciju drugih u OsmAndu.<br/><br/>Aplikacija koristi Telegram API, pa vam je potreban Telegram nalog.</string>
<string name="shared_string_second_short">sek</string>
<string name="shared_string_minute_short">min</string>
<string name="shared_string_hour_short">č</string>
<string name="si_nm_h">Nautičkih milja na sat (čvorovi)</string>
<string name="nm_h">nmi/č</string>
<string name="shared_string_welcome">Dobrodošli</string>
<string name="shared_string_authorization_descr">Unesite vaš telefonski broj Telegrama u međunarodnom formatu</string>
<string name="shared_string_authorization">Autorizacija</string>
<string name="active_chats">Aktivna ćaskanja</string>
<string name="show_users_on_map">Prikažite korisnike na mapi</string>
<string name="install_osmand">Instalirajte OsmAnd</string>
<string name="install_osmand_dialog_message">Prvo morate instalirati besplatnu ili plaćenu verziju OsmAnda</string>
<string name="osmand_logo">Logo OsmAnda</string>
<string name="process_service">Usluga OsmAnd Pratioca</string>
<string name="sharing_location">Deljenje lokacije</string>
<string name="share_location">Deli lokaciju</string>
<string name="shared_string_distance">Rastojanje</string>
<string name="osmand_service_descr">OsmAnd Pratioc radi u pozadini sa isključenim ekranom.</string>
<string name="location_service_no_gps_available">Izaberite jednog od dobavljača lokacije da bi deliti vašu lokaciju.</string>
<string name="gps_not_available">Uključite „Lokaciju“ u sistemskim podešavanjima</string>
<string name="no_location_permission">Aplikaciji nedostaje dozvola za pristup podacima o lokaciji.</string>
<string name="not_logged_in">Niste prijavljeni</string>
<string name="gps_network_not_enabled">Uključiti „Lokaciju“\?</string>
<string name="closing">Zatvaranje</string>
<string name="logging_out">Odjavljivanje</string>
<string name="initialization">Pokretanje</string>
<string name="shared_string_logout">Odjaviti se</string>
<string name="shared_string_login">Prijavite se</string>
<string name="password_descr">Telegram lozinka</string>
<string name="enter_password">Unesite lozinku</string>
<string name="authentication_code_descr">Telegram vam je poslao kod za OsmAnd radi prijave na vaš nalog.</string>
<string name="authentication_code">Validacioni kod</string>
<string name="enter_code">Unesite kod</string>
<string name="shared_string_password">Lozinka</string>
<string name="phone_number_descr">Broj telefona u međunarodnom formatu</string>
<string name="phone_number_title">Broj telefona</string>
<string name="app_name">OsmAnd Onlajn GPS Pratioc</string>
<string name="show_on_map">Prikaži na mapi</string>
<string name="start_location_sharing">Deli lokaciju</string>
<string name="my_location_search_hint">Pretražite: Grupu ili kontakt</string>
<string name="location_sharing_description">Izaberite kontakte i grupe sa kojima želite da delite lokaciju.</string>
<string name="set_time">Podesite vreme</string>
<string name="set_time_description">Podesite vreme za koji će izabrani kontakti i grupe videti vašu lokaciju u realnom vremenu.</string>
<string name="visible_time_for_all">Vidljivo vreme za sve</string>
<string name="hours_format">%1$ č</string>
<string name="minutes_format">%1$ m</string>
<string name="hours_and_minutes_format">%1$ č %2$ m</string>
<string name="set_visible_time_for_all">Podesite vidljivo vreme za sve</string>
<string name="enter_authentication_code">Unesite kod za validaciju</string>
<string name="enter_phone_number">Unesite broj telefona</string>
<string name="do_not_have_telegram">Nemam Telegram nalog</string>
<string name="already_registered_in_telegram">Potreban vam je registrovani Telegram nalog i broj telefona</string>
<string name="get_telegram_after_creating_account">Tada možete da koristite ovu aplikaciju.</string>
<string name="get_telegram_description_continue">Instalirajte Telegram i otvorite nalog.</string>
<string name="get_telegram_account_first">Za deljenje lokacije potreban vam je Telegram nalog.</string>
<string name="get_telegram_title">Registracija u Telegramu</string>
<string name="shared_string_bot">Bot</string>
<string name="shared_string_live">Uživo</string>
<string name="open_osmand">Otvori OsmAnd</string>
<string name="turn_off_location_sharing">Isključite deljenje lokacije</string>
<string name="stop_sharing_all">Deljenje je uključeno (isključite)</string>
<string name="expire_at">Ističe</string>
<string name="sharing_time">Vreme deljenja</string>
<string name="gps_and_location">Pozicija</string>
<string name="send_my_location">Pošalji moju lokaciju</string>
<string name="send_my_location_desc">Podesite minimalni interval za deljenje lokacije.</string>
<string name="stale_location">Nepomičan</string>
<string name="stale_location_desc">Poslednji put kada se kontakt pomerio.</string>
<string name="location_history">Istorija lokacije</string>
<string name="location_history_desc">Sakrijte kontakte koji se nisu pomerili u datom vremenu.</string>
<string name="osmand_connect">Osmand veza</string>
<string name="osmand_connect_desc">Odaberite verziju OsmAnda koju OsmAnd pratioc koristi za prikazivanje pozicija.</string>
<string name="in_time">u %1$</string>
<string name="shared_string_account">Nalog</string>
<string name="connected_account">Povezani nalog</string>
<string name="logout_help_desc">Kako isključiti OsmAnd pratioca iz Telegrama</string>
<string name="disconnect_from_telegram">Kako isključiti OsmAnd pratioca iz Telegrama</string>
<string name="disconnect_from_telegram_desc">Da biste opozvali pristup deljenju lokacije. Otvorite Telegram, idite na Podešavanja → Privatnost i bezbednost → Sesije i prekinete sesiju OsmAnd pratioca.</string>
<string name="logout_no_internet_msg">Povežite se na Internet kako biste se pravilno odjavili iz Telegrama.</string>
<string name="shared_string_group">Grupa</string>
<string name="last_response">Poslednji odgovor</string>
<string name="time_ago">pre</string>
<string name="turn_off_all">Isključi sve</string>
<string name="disable_all_sharing">Onemogući svako deljenje</string>
<string name="disable_all_sharing_desc">Isključuje deljenje lokacije prema svim izabranim čatovima (%1$).</string>
<string name="choose_osmand">Izaberite verziju OsmAnda koju želite da koristite</string>
<string name="choose_osmand_desc">Izaberite verziju OsmAnda gde će se kontakti prikazati na mapi.</string>
<string name="shared_string_sort_by">Sortiraj po</string>
<string name="by_group">Po grupi</string>
<string name="by_name">Po imenu</string>
<string name="by_distance">Po udaljenosti</string>
<string name="logout_from_osmand_telegram">Odjaviti se sa OsmAnd pratioca\?</string>
<string name="logout_from_osmand_telegram_descr">Jeste li sigurni da se želite odjaviti sa OsmAnd pratioca tako da ne možete da delite lokaciju ili vidite lokaciju drugih\?</string>
<string name="live_now_description">Kontakti i grupe dele lokaciju vama.</string>
<string name="share_location_as">Deljenje lokacije kao</string>
<string name="add_device">Dodajte uređaj</string>
<string name="no_internet_connection">Nema internet konekcije</string>
<string name="no_gps_connection">Nema GPS veze</string>
<string name="location_sharing_status">Deljenje: %1$</string>
<string name="sharing_status">Deljenje statusa</string>
<string name="last_available_location">Poslednja dostupna lokacija</string>
<string name="re_send_location">Ponovo pošalji lokaciju</string>
<string name="not_found_yet">Još nije pronađeno</string>
<string name="not_sent_yet">Još nije poslato</string>
<string name="shared_string_later">Kasnije</string>
<string name="go_to_settings">Idi na Podešavanja</string>
<string name="sharing_in_background">Deljenje u pozadini</string>
<string name="battery_optimization_description">Isključite optimizaciju baterije za OsmAnd pratilac tako da se ne isključi iznenada kad je u pozadini.</string>
<string name="background_work">Rad u pozadini</string>
<string name="background_work_description">Promenite podešavanja za optimizaciju baterije da biste stabilizovali deljenje lokacije.</string>
<string name="connecting_to_the_internet">Povezivanje sa Internetom</string>
<string name="searching_for_gps">Pozicioniranje…</string>
<string name="initializing">Pokretanje</string>
<string name="sending_location_messages">Lokacija se šalje</string>
<string name="waiting_for_response_from_telegram">Čeka se odgovor iz Telegrama</string>
<string name="not_possible_to_send_to_telegram_chats">Nije moguće poslati u Telegram četove:</string>
<string name="successfully_sent_and_updated">Uspešno poslato i ažurirano</string>
<string name="last_updated_location">Poslednja ažurirana lokacija:</string>
<string name="share_location_as_description">Ako želite da povežete više uređaja sa jednim nalogom telegrama, trebate koristiti drugi uređaj da bi delili vašu lokaciju.</string>
<string name="share_location_as_description_second_line">Možete da kreirate i vidite ID uređaja u telegram klijentu koristeći %1$ čat bot. %2$</string>
<string name="device_name">Ime uređaja</string>
<string name="device_name_cannot_be_empty">Ime uređaja ne može biti prazno</string>
<string name="device_name_is_too_long">Ime uređaja predugo</string>
<string name="enter_device_name_description">Imenujte vaš novi uređaj sa maksimalno 200 simbola.</string>
<string name="error_adding_new_device">Nije moguće dodati novi uređaj</string>
<string name="device_added_successfully">%1$ dodato.</string>
<string name="enter_another_device_name">Izaberite ime koje niste već koristili</string>
<string name="last_update_from_telegram">Poslednje ažuriranje od Telegrama</string>
<string name="map_and_text">Mapa i tekst</string>
<string name="shared_string_text">Tekst</string>
<string name="send_location_as_descr">Odaberite kako će izgledati poruke sa vašom lokacijom.</string>
<string name="send_location_as">Pošalji lokaciju kao</string>
<string name="start_date">Početni datum</string>
<string name="end_date">Krajnji datum</string>
<string name="open_in_osmand">Prikaži u OsmAndu</string>
<string name="time_on_the_move">Vreme kretanja</string>
<string name="monitoring_is_disabled">Praćenje je onemogućeno</string>
<string name="monitoring_is_enabled">Praćenje je omogućeno</string>
<string name="shared_string_sent">Poslato</string>
<string name="gps_points">GPS tačke</string>
<string name="shared_string_collected">Prikupljeno</string>
<string name="shared_string_date">Datum</string>
<string name="points_size">%1$ tačaka</string>
<string name="gps_points_in_buffer">poslato (%1$ u baferu)</string>
<string name="please_update_osmand">Ažurirajte OsmAnd da biste videli podatke na mapi</string>
<string name="show_gps_points_descr">Prikaži količinu prikupljenih i poslatih GPS tačaka.</string>
<string name="show_gps_points">Pokaži GPS tačke</string>
<string name="received_gps_points">Primljene GPKS tačke: %1$</string>
<string name="how_it_works">Kako radi</string>
<string name="osmand_privacy_policy">OsmAnd politika privatnosti</string>
<string name="telegram_privacy_policy">Politika privatnosti Telegrama</string>
<string name="shared_string_accept">Prihvati</string>
<string name="privacy_policy_agree">Klikom na „Nastavi“ prihvatate uslove politike privatnosti Telegrama i OsmAnda.</string>
<string name="privacy_policy_telegram_client">OsmAnd pratilac je jedan od klijenata koji koriste otvorenu platformu Telegram. Vaši kontakti mogu da koriste bilo koji drugi Telegram klijent.</string>
<string name="privacy_policy_use_telegram">Telegram (aplikacija za razmenu poruka) koristi se za povezivanje i komunikaciju sa ljudima.</string>
<string name="shared_string_telegram">Telegram</string>
<string name="app_name_short">OsmAnd pratilac</string>
<string name="timeline_description">Omogućite praćenje da biste sačuvali sve lokacije u istoriji.</string>
<string name="location_recording_enabled">Snimanje lokacije omogućeno</string>
<string name="disable_monitoring">Onemogućite praćenje</string>
<string name="timeline_available_for_free_now">Vremenska linija je funkcija koja je sada dostupna besplatno.</string>
<string name="type_contact_or_group_name">Unesite ime kontakta ili grupe</string>
<string name="search_contacts_descr">Pretraga po svim vašim grupama i kontaktima.</string>
<string name="search_contacts">Pretraga kontakta</string>
<string name="bearing">Usmerenje</string>
<string name="precision">Preciznost</string>
<string name="direction">Smer</string>
<string name="privacy">Privatnost</string>
<string name="proxy">Proksi</string>
<string name="proxy_settings">Podešavanja proksija</string>
<string name="proxy_disconnected">Prekinut</string>
<string name="proxy_connected">Povezan</string>
<string name="proxy_type">Tip proksija</string>
<string name="shared_string_connection">Veza</string>
<string name="proxy_server">Server</string>
<string name="proxy_port">Port</string>
<string name="proxy_credentials">Akreditivi</string>
<string name="proxy_username">Korisničko ime</string>
<string name="proxy_password">Lozinka</string>
<string name="proxy_key">Ključ</string>
<string name="gpx_settings">GPX podešavanja</string>
<string name="min_logging_speed_descr">Filter: nema zapisivanja ispod odabrane brzine</string>
<string name="min_logging_speed">Minimalna brzina zapisivanja</string>
<string name="min_logging_accuracy_descr">Filter: Nema zapisa dok se ne dostigne ova tačnost</string>
<string name="min_logging_accuracy">Minimalna tačnost evidentiranja</string>
<string name="min_logging_distance_descr">Filter: minimalna udaljenost za evidentiranje nove tačke</string>
<string name="min_logging_distance">Minimalna udaljenost evidentiranja</string>
<string name="timeline_no_data">Nema podataka</string>
<string name="timeline_no_data_descr">Nemamo prikupljene podatke za izabrani dan</string>
<string name="start_end_date">Početni — Krajnji datum</string>
<string name="set_time_timeline_descr">Izaberite vreme za prikaz</string>
<string name="shared_string_start">Početak</string>
<string name="shared_string_end">Kraj</string>
<string name="saved_messages">Sačuvane poruke</string>
<string name="unit_of_speed_system">Jedinica brzine</string>
<string name="unit_of_speed_system_descr">Definišite jedinicu brzine.</string>
<string name="time_zone">Vremenska zona</string>
<string name="time_zone_descr">Izaberite vremensku zonu koja će se prikazati u porukama lokacije.</string>
<string name="buffer_time">Vreme isteka bafera</string>
<string name="buffer_time_descr">Maksimalno vreme za skladištenje tačaka u bafer</string>
<string name="status_widget_title">Status Tragača OsmAnda</string>
<string name="shared_string_suggested">Predloženo</string>
<string name="back_to_osmand">Povratak na OsmAnd</string>
<string name="duration_ago">Pre %1$</string>
<string name="last_response_duration">Poslednji odgovor: pre %1$</string>
<string name="last_update_from_telegram_duration">Poslednje ažuriranje iz Telegrama: pre %1$</string>
<string name="last_response_date">Poslednji odgovor: %1$</string>
<string name="last_update_from_telegram_date">Poslednje ažuriranje iz Telegrama: %1$</string>
<string name="shared_string_error_short">Greška</string>
<string name="shared_string_export">Izvezi</string>
<string name="logcat_buffer">Logcat bafer</string>
<string name="logcat_buffer_descr">Proverite i podelite detaljne zapise aplikacije</string>
<string name="send_report">Pošalji izveštaj</string>
</resources>

View file

@ -109,7 +109,7 @@
<string name="get_telegram_description_continue">Kérjük, telepítse a Telegramot és hozzon létre egy fiókot.</string>
<string name="get_telegram_after_creating_account">Utána használhatja ezt az alkalmazást.</string>
<string name="shared_string_all">Minden</string>
<string name="shared_string_off">Kikapcsolás</string>
<string name="shared_string_off">Kikapcsolva</string>
<string name="hours_and_minutes_format">%1$d óra %2$d perc</string>
<string name="minutes_format">%1$d perc</string>
<string name="shared_string_install">Telepítés</string>

View file

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M12,22C17.5228,22 22,17.5228 22,12C22,6.4771 17.5228,2 12,2C6.4771,2 2,6.4771 2,12C2,17.5228 6.4771,22 12,22ZM11,14V7H13V14H11ZM11,18V16H13V18H11Z"
android:fillColor="#ffffff"
android:fillType="evenOdd"/>
</vector>

View file

@ -0,0 +1,18 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M20,12C20,15.4738 17.7859,18.4304 14.692,19.5358L14.9449,21.5593C19.0304,20.3022 22,16.4979 22,12C22,6.4771 17.5228,2 12,2C6.4771,2 2,6.4771 2,12C2,16.4979 4.9696,20.3022 9.0551,21.5593L9.308,19.5358C6.2141,18.4304 4,15.4738 4,12C4,7.5817 7.5817,4 12,4C16.4183,4 20,7.5817 20,12Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M18,12C18,14.4466 16.5357,16.5511 14.4356,17.485L14.1701,15.3607C15.2713,14.6482 16,13.4092 16,12C16,9.7909 14.2091,8 12,8C9.7909,8 8,9.7909 8,12C8,13.4092 8.7287,14.6482 9.8299,15.3607L9.5644,17.485C7.4643,16.5511 6,14.4466 6,12C6,8.6863 8.6863,6 12,6C15.3137,6 18,8.6863 18,12Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M14,12C14,13.1046 13.1046,14 12,14C10.8954,14 10,13.1046 10,12C10,10.8954 10.8954,10 12,10C13.1046,10 14,10.8954 14,12Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M11,15V22H13V15H11Z"
android:fillColor="#ffffff"/>
</vector>

View file

@ -0,0 +1,15 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M22.0001,12C22.0001,13.9623 21.4349,15.7926 20.4583,17.337L18.9991,15.8777C19.6369,14.7291 20.0001,13.407 20.0001,12C20.0001,7.5817 16.4183,4 12.0001,4C10.5931,4 9.271,4.3632 8.1223,5.0009L6.6631,3.5417C8.2075,2.5652 10.0378,2 12.0001,2C17.5229,2 22.0001,6.4771 22.0001,12Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M18.0001,12C18.0001,12.8478 17.8242,13.6545 17.507,14.3857L15.9203,12.7989C15.9726,12.5407 16.0001,12.2736 16.0001,12C16.0001,9.7909 14.2092,8 12.0001,8C11.7265,8 11.4593,8.0275 11.2012,8.0798L9.6144,6.493C10.3456,6.1758 11.1523,6 12.0001,6C15.3138,6 18.0001,8.6863 18.0001,12Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M14.3449,16.759L14.4357,17.485C14.5806,17.4206 14.7225,17.3506 14.8611,17.2752L16.3203,18.7344C15.8138,19.0599 15.2682,19.33 14.692,19.5358L14.945,21.5593C15.9623,21.2463 16.9105,20.7753 17.7609,20.1749L21.293,23.7071L22.7072,22.2928L2.7072,2.2929L1.293,3.7071L3.8251,6.2392C2.6754,7.8677 2.0001,9.855 2.0001,12C2.0001,16.4979 4.9696,20.3022 9.0552,21.5593L9.3081,19.5358C6.2141,18.4304 4.0001,15.4738 4.0001,12C4.0001,10.4087 4.4647,8.9258 5.2657,7.6798L6.7248,9.1389C6.2626,9.9894 6.0001,10.964 6.0001,12C6.0001,14.4466 7.4644,16.5511 9.5644,17.485L9.83,15.3607C8.7288,14.6482 8.0001,13.4092 8.0001,12C8.0001,11.5256 8.0826,11.0705 8.2342,10.6483L10.0555,12.4696C10.2305,13.197 10.8031,13.7695 11.5305,13.9446L12.5859,15H11.0001V22H13.0001V15.4142L14.3449,16.759Z"
android:fillColor="#ffffff"/>
</vector>

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M6,18V17.9725C3.75,17.7238 2,15.8163 2,13.5C2,11.0147 4.0147,9 6.5,9C6.5998,9 6.6989,9.0033 6.7971,9.0097C7.8332,7.2109 9.7752,6 12,6C15.3137,6 18,8.6863 18,12C18,12.0574 17.9992,12.1146 17.9976,12.1716C18.3111,12.0605 18.6485,12 19,12C20.6569,12 22,13.3431 22,15C22,16.6569 20.6569,18 19,18H6Z"
android:fillColor="#ffffff"/>
</vector>

View file

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M6,17.9725V18H19C20.6569,18 22,16.6569 22,15C22,13.3431 20.6569,12 19,12C18.6485,12 18.3111,12.0605 17.9976,12.1716C17.9992,12.1146 18,12.0574 18,12C18,8.6863 15.3137,6 12,6C9.7752,6 7.8332,7.2109 6.7971,9.0097C6.6989,9.0033 6.5998,9 6.5,9C4.0147,9 2,11.0147 2,13.5C2,15.8163 3.75,17.7238 6,17.9725ZM11,13V8H13V13H11ZM11,17V15H13V17H11Z"
android:fillColor="#ffffff"
android:fillType="evenOdd"/>
</vector>

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M14,18V15H16.5L12.5,10L8.5,15H11V18H6V17.9725C3.75,17.7238 2,15.8163 2,13.5C2,11.0147 4.0147,9 6.5,9C6.5998,9 6.6989,9.0033 6.7971,9.0097C7.8332,7.2109 9.7752,6 12,6C15.3137,6 18,8.6863 18,12C18,12.0574 17.9992,12.1146 17.9976,12.1716C18.3111,12.0605 18.6485,12 19,12C20.6569,12 22,13.3431 22,15C22,16.6569 20.6569,18 19,18H14Z"
android:fillColor="#ffffff"/>
</vector>

View file

@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M6,18V17.9725C3.75,17.7238 2,15.8163 2,13.5C2,11.0147 4.0147,9 6.5,9C6.5998,9 6.6989,9.0033 6.7971,9.0097C7.8332,7.2109 9.7752,6 12,6C15.3137,6 18,8.6863 18,12C18,12.0574 17.9992,12.1146 17.9976,12.1716C18.3111,12.0605 18.6485,12 19,12C20.6569,12 22,13.3431 22,15C22,16.6569 20.6569,18 19,18H6Z"
android:fillColor="#6C19FF"/>
<path
android:pathData="M11,15H8.5L12.5,9.5L16.5,15H14V20H11V15Z"
android:fillColor="#FFC30D"/>
</vector>

View file

@ -0,0 +1,15 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M14,2L20,8H16C14.8954,8 14,7.1046 14,6V2Z"
android:strokeAlpha="0.5"
android:fillColor="#ffffff"
android:fillAlpha="0.5"/>
<path
android:pathData="M4,4C4,2.8954 4.8954,2 6,2H14V6C14,7.1046 14.8954,8 16,8H20V20C20,21.1046 19.1046,22 18,22H6C4.8954,22 4,21.1046 4,20V4ZM11,18H13V13H15L12,9L9,13H11V18Z"
android:fillColor="#ffffff"
android:fillType="evenOdd"/>
</vector>

View file

@ -0,0 +1,37 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M12,5C12.5523,5 13,4.5523 13,4H17V8.0549C16.6717,8.0186 16.338,8 16,8C11.0294,8 7,12.0294 7,17V4H11C11,4.5523 11.4477,5 12,5Z"
android:strokeAlpha="0.3"
android:fillColor="#ffffff"
android:fillAlpha="0.3"/>
<path
android:pathData="M7,17C7,18.0519 7.1805,19.0617 7.5121,20H7V17Z"
android:strokeAlpha="0.3"
android:fillColor="#ffffff"
android:fillAlpha="0.3"/>
<path
android:pathData="M17,2.01L7,2C5.9,2 5,2.9 5,4V20C5,21.1 5.9,22 7,22H8.5155C8.1025,21.383 7.7638,20.7121 7.5121,20H7V4H11C11,4.5523 11.4477,5 12,5C12.5523,5 13,4.5523 13,4H17V8.0549C17.6935,8.1316 18.3632,8.287 19,8.5121V4C19,2.9 18.1,2.01 17,2.01Z"
android:strokeAlpha="0.7"
android:fillColor="#ffffff"
android:fillAlpha="0.7"/>
<path
android:pathData="M16,21C18.2091,21 20,19.2091 20,17C20,14.7909 18.2091,13 16,13C13.7909,13 12,14.7909 12,17H14L11,20L8,17H10C10,13.6863 12.6863,11 16,11C19.3137,11 22,13.6863 22,17C22,20.3137 19.3137,23 16,23C14.598,23 13.3082,22.5191 12.2868,21.7132L13.7159,20.2841C14.3635,20.7354 15.1508,21 16,21Z"
android:fillType="evenOdd">
<aapt:attr name="android:fillColor">
<gradient
android:gradientRadius="6.36396"
android:centerX="16"
android:centerY="17"
android:type="radial">
<item android:offset="0" android:color="#FFFFFFFF"/>
<item android:offset="0.8125" android:color="#FFFFFFFF"/>
<item android:offset="1" android:color="#00FFFFFF"/>
</gradient>
</aapt:attr>
</path>
</vector>

View file

@ -0,0 +1,15 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M6,3C4.8954,3 4,3.8954 4,5V7H20C20,5.8954 19.1046,5 18,5H13L11.4,3H6Z"
android:strokeAlpha="0.5"
android:fillColor="#ffffff"
android:fillAlpha="0.5"/>
<path
android:pathData="M2,7C2,5.8954 2.8954,5 4,5H10L12,7H20C21.1046,7 22,7.8954 22,9V19C22,20.1046 21.1046,21 20,21H4C2.8954,21 2,20.1046 2,19V7ZM17,12C17,13.1046 16.1046,14 15,14C13.8954,14 13,13.1046 13,12C13,10.8954 13.8954,10 15,10C16.1046,10 17,10.8954 17,12ZM10,17.5V19H20V17.5C20,15.5 17,15 15,15C13,15 10,15.5 10,17.5Z"
android:fillColor="#ffffff"
android:fillType="evenOdd"/>
</vector>

View file

@ -0,0 +1,7 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners
android:bottomLeftRadius="@dimen/list_item_button_padding"
android:topLeftRadius="@dimen/list_item_button_padding" />
<solid android:color="@color/list_background_color_dark" />
</shape>

View file

@ -0,0 +1,11 @@
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@color/active_buttons_and_links_trans_light">
<item android:id="@android:id/mask">
<shape android:shape="rectangle">
<corners
android:bottomLeftRadius="@dimen/list_item_button_padding"
android:topLeftRadius="@dimen/list_item_button_padding" />
<solid android:color="@color/active_color_primary_light" />
</shape>
</item>
</ripple>

View file

@ -1,15 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/background">
<shape android:shape="rectangle">
<solid android:color="#4d007eb3" />
<corners android:radius="30dp" />
</shape>
</item>
<item android:id="@+id/progress">
<shape android:shape="rectangle">
<solid android:color="@color/color_white" />
<corners android:radius="30dp" />
</shape>
</item>
</layer-list>

View file

@ -1,13 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<item android:id="@+id/thump">
<shape android:shape="oval">
<size
android:width="12dp"
android:height="12dp" />
<solid android:color="@color/profile_icon_color_blue_light" />
</shape>
</item>
</layer-list>

View file

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<size
android:width="2dp"
android:height="2dp" />
<solid android:color="#17181A" />
</shape>

View file

@ -62,18 +62,12 @@
osmand:typeface="@string/font_roboto_medium"
tools:text="Normal" />
<SeekBar
android:id="@+id/seek_bar_arrival"
style="@style/Widget.AppCompat.SeekBar.Discrete"
<com.google.android.material.slider.Slider
android:id="@+id/arrival_slider"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/pages_item_margin"
android:maxHeight="2dp"
android:paddingTop="11dp"
android:paddingBottom="11dp"
osmand:tickMark="@drawable/seekbar_tickmark_announcement_time"
tools:max="3"
tools:progress="1" />
android:layout_marginLeft="@dimen/content_padding"
android:layout_marginRight="@dimen/content_padding" />
<View
android:id="@+id/divider"

View file

@ -2,6 +2,7 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="wrap_content"
android:layout_width="match_parent"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical">
<View
@ -58,7 +59,7 @@
android:layout_gravity="center_vertical|start"
android:textColor="?android:textColorPrimary"
android:textSize="@dimen/default_list_text_size_large"
android:text="Item title"/>
tools:text="Item title"/>
</LinearLayout>

View file

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@ -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" />

View file

@ -5,8 +5,10 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="@dimen/content_padding"
android:paddingStart="@dimen/content_padding"
android:paddingLeft="@dimen/content_padding"
android:paddingTop="@dimen/content_padding"
android:paddingRight="@dimen/content_padding"
android:paddingEnd="@dimen/content_padding">
<View
@ -43,4 +45,9 @@
</LinearLayout>
<View
android:id="@+id/space"
android:layout_width="match_parent"
android:layout_height="@dimen/content_padding" />
</LinearLayout>

View file

@ -2,6 +2,7 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:paddingLeft="?dialogPreferredPadding"
android:paddingRight="?dialogPreferredPadding"
@ -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"/>
<TextView

View file

@ -4,6 +4,7 @@
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">
<com.google.android.material.appbar.AppBarLayout
@ -51,7 +52,7 @@
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:background="@null"
android:text="Filling station"
tools:text="Filling station"
android:textColor="@color/color_white"
android:textSize="@dimen/default_desc_text_size"/>

View file

@ -94,7 +94,7 @@
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:background="@null"
android:text="Split interval:"
tools:text="Split interval:"
android:textColor="?android:attr/textColorPrimary"
android:textSize="@dimen/default_desc_text_size"
osmand:typeface="@string/font_roboto_medium" />

View file

@ -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" />

View file

@ -1,9 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:osmand="http://schemas.android.com/apk/res-auto"
<LinearLayout xmlns:osmand="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:tools="http://schemas.android.com/tools"
android:gravity="center_vertical"
android:minHeight="48dp"
android:orientation="horizontal"
@ -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"/>
<TextView
@ -44,7 +44,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:text="Select roads you want to avoid during navigation"
tools:text="Select roads you want to avoid during navigation"
android:textSize="@dimen/default_desc_text_size"/>
</LinearLayout>

View file

@ -118,7 +118,7 @@
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" />

View file

@ -2,6 +2,7 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
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">
@ -37,6 +38,6 @@
android:layout_height="wrap_content"
android:layout_weight="0"
android:background="?attr/selectableItemBackground"
android:text="UNDO"
tools:text="UNDO"
android:textColor="@color/popup_text_color"/>
</LinearLayout>

View file

@ -4123,7 +4123,11 @@
<string name="shared_string_route_line">خط المسار</string>
<string name="route_line_use_map_style_appearance">سيستخدم خط الطريق %1$s المحدد في نمط الخريطة المحدد: %2$s.</string>
<string name="specify_color_for_map_mode">حدد لونًا لوضع الخريطة: %1$s.</string>
<string name="release_4_0_beta">• تم نقل تحديثات OsmAnd Live إلى \"التنزيلات&gt; التحديثات\"
<string name="release_4_0_beta">• خيار مضاف لتنزيل خطوط الكنتور بالقدم
\n
\n• مخطط الطريق الأفقي: علامات تبويب tabs مضافة للتبديل بين النقاط أو الرسوم البيانية
\n
\n• تم نقل تحديثات OsmAnd Live إلى \"التنزيلات&gt; التحديثات\"
\n
\n • يمكن تلوين المسارات الآن حسب الارتفاع، السرعة أو المنحدر.
\n
@ -4154,4 +4158,8 @@
<string name="announce_when_exceeded">الإعلان عند التجاوز</string>
<string name="user_points">نقاط المستخدم</string>
<string name="output">الإخراج</string>
<string name="srtm_unit_format">تنسيق وحدة خطوط الكنتور</string>
<string name="srtm_download_list_help_message">OsmAnd يوفر بيانات الخطوط الكنتورية بالأمتار والقدم. ستحتاج إلى إعادة تنزيل الملف لتغيير التنسيق.</string>
<string name="srtm_download_single_help_message">الرجاء تحديد التنسيق المطلوب. ستحتاج إلى إعادة تنزيل الملف لتغيير التنسيق.</string>
<string name="shared_string_feet">قدم</string>
</resources>

File diff suppressed because it is too large Load diff

View file

@ -1088,7 +1088,7 @@
<string name="lang_ja">Японская</string>
<string name="lang_ko">Карэйская</string>
<string name="lang_lv">Латышская</string>
<string name="lang_lt">Летувісская</string>
<string name="lang_lt">Летувіская</string>
<string name="lang_mr">Маратхі</string>
<string name="lang_no">Нарвежская (Bokmål)</string>
<string name="lang_pl">Польская</string>

View file

@ -4057,7 +4057,11 @@
<string name="customize_route_line">Routenlinie anpassen</string>
<string name="shared_string_route_line">Routenlinie</string>
<string name="specify_color_for_map_mode">Legen Sie die Farbe für den Kartenmodus fest: %1$s.</string>
<string name="release_4_0_beta">• OsmAnd Live-Updates nach \"Downloads &gt; Updates\" verschoben
<string name="release_4_0_beta">• 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 &gt; Updates\" verschoben
\n
\n• Tracks können nun nach Höhe, Geschwindigkeit oder Steigung eingefärbt werden.
\n
@ -4089,4 +4093,8 @@
<string name="announce_when_exceeded">Meldung bei Überschreitung</string>
<string name="user_points">Anwenderpunkte</string>
<string name="output">Leistung</string>
<string name="shared_string_feet">Fuß</string>
<string name="srtm_unit_format">Einheit der Höhenlinien</string>
<string name="srtm_download_list_help_message">OsmAnd liefert Höhenlinien-Daten in Metern und Fuß. Sie müssen die Datei erneut herunterladen, um das Format zu ändern.</string>
<string name="srtm_download_single_help_message">Bitte wählen Sie das gewünschte Format. Sie müssen die Datei erneut herunterladen, um das Format zu ändern.</string>
</resources>

View file

@ -4054,7 +4054,11 @@
<string name="customize_route_line">Alĝustigi linion de kurso</string>
<string name="shared_string_route_line">Linio de kurso</string>
<string name="route_line_use_map_style_appearance">Por la linio de kurso estos uzata %1$s difinita por la mapstilo: %2$s.</string>
<string name="release_4_0_beta">• ĝisdatigoj OsmAnd Live movitaj al “elŝutoj” → “ĝisdatigoj”
<string name="release_4_0_beta">• 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 @@
<string name="exit_number">Numero de elirejo</string>
<string name="user_points">Poentoj de uzanto</string>
<string name="output">Eligo</string>
<string name="shared_string_feet">futoj</string>
<string name="srtm_unit_format">Unuo por nivelkurboj</string>
<string name="srtm_download_list_help_message">OsmAnd liveras nivelkurbojn en metroj kaj en futoj. Ve devos reelŝuti la dosieron por ŝanĝi la formon.</string>
<string name="srtm_download_single_help_message">Elektu la deziratan formon. Vi devos reelŝuti la dosieron por ŝanĝi la formon.</string>
</resources>

View file

@ -3915,4 +3915,17 @@
<string name="poi_pilates">Pilates</string>
<string name="poi_jiu_jitsu">Ju-jitsu</string>
<string name="poi_karate">Karaté</string>
<string name="poi_zurkhaneh_sport">Gymnase Zurkhaneh</string>
<string name="poi_cliff_diving">Plongeon du haut d\'une falaise</string>
<string name="poi_hoops">Anneaux</string>
<string name="poi_club_social">Club social</string>
<string name="poi_plateau">Plateau</string>
<string name="poi_speedway">Voie express</string>
<string name="poi_gladed_yes">Satisfait : oui</string>
<string name="poi_camp_pitch">Emplacement de camping</string>
<string name="poi_mobile_library">Arrêt pour la bibliothèque mobile</string>
<string name="poi_office_diplomatic">Bureau diplomatique</string>
<string name="poi_horseshoes">Fers à cheval</string>
<string name="poi_cycle_polo">Polo-vélo</string>
<string name="poi_bay_filter">Type de baie</string>
</resources>

View file

@ -3275,7 +3275,7 @@
<string name="add_new_profile_q">Ajouter le profil \'%1$s\' \?</string>
<string name="save_heading">Inclure la direction</string>
<string name="save_heading_descr">Inclure la direction de chaque point lors de l\'enregistrement d\'une trace.</string>
<string name="rendering_attr_showCycleNodeNetworkRoutes_name">Afficher le réseau de nœuds de pistes cyclables</string>
<string name="rendering_attr_showCycleNodeNetworkRoutes_name">Afficher les itinéraires cyclables du réseau de nœuds</string>
<string name="rendering_value_walkingRoutesOSMCNodes_name">Nœuds de transport</string>
<string name="personal_category_name">Personnel</string>
<string name="ltr_or_rtl_combine_via_bold_point">%1$s • %2$s</string>
@ -4040,9 +4040,13 @@
<string name="lost_data_warning">Toutes les données non enregistrées seront perdues.</string>
<string name="show_start_dialog">Afficher la boîte de dialogue de démarrage</string>
<string name="trip_recording_show_start_dialog_setting">Si désactivé, l\'enregistrement débutera dès appui sur le gadget ou dans le menu (sans demande de confirmation).</string>
<string name="release_4_0_beta">- Les mises à jour d\'OsmAnd Live ont été déplacées vers \"Téléchargements &gt; Mises à jour\"
<string name="release_4_0_beta">- 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 donglets pour basculer entre Points et Graphs
\n
\n- Les mises à jour d\'OsmAnd Live ont été déplacées vers \"Téléchargements &gt; 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 @@
<string name="exit_number">Numéro de sortie</string>
<string name="announce_when_exceeded">Annoncer en cas de dépassement</string>
<string name="output">Sortie</string>
<string name="srtm_download_list_help_message">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.</string>
<string name="srtm_download_single_help_message">Veuillez sélectionner le format souhaité. Vous devrez à nouveau télécharger le fichier pour modifier le format.</string>
<string name="srtm_unit_format">Format d\'unité pour les courbes de niveau</string>
<string name="shared_string_feet">pieds</string>
</resources>

View file

@ -13,8 +13,8 @@
<string name="shared_string_help">Súgó</string>
<string name="accessibility_mode">Akadálymentes mód</string>
<string name="accessibility_mode_descr">Bekapcsolja a fogyatékkal élőknek szóló funkciókat.</string>
<string name="shared_string_on">Be</string>
<string name="shared_string_off">Kikapcsolás</string>
<string name="shared_string_on">Bekapcsolva</string>
<string name="shared_string_off">Kikapcsolva</string>
<string name="accessibility_default">Android rendszerbeállítások szerint</string>
<string name="backToMenu">Vissza a menübe</string>
<string name="zoomOut">Kicsinyít</string>
@ -1318,7 +1318,7 @@
<string name="shared_string_more">Bővebben…</string>
<string name="route_descr_destination">Célpont</string>
<string name="local_index_description">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):</string>
<string name="speed_limit_exceed">Sebességkorlátozás-tolerancia</string>
<string name="speed_limit_exceed">Sebességkorlátozás túllépésének tűréshatára</string>
<string name="speed_limit_exceed_message">Válassza ki a sebességkorlátozás tűréshatárát, amely fölött hangos figyelmeztetést fog kapni.</string>
<string name="fav_point_emoticons_message">A Kedvenc hely át lett nevezve erre: %1$s, hogy a hangulatjeleket tartalmazó szöveget fájlba lehessen menteni.</string>
<string name="print_route">Útvonal nyomtatása</string>
@ -1470,7 +1470,7 @@
<string name="shared_string_release">Megjelent</string>
<string name="days_behind">napos</string>
<string name="welmode_download_maps">Térképek letöltése</string>
<string name="confirm_usage_speed_cameras">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.</string>
<string name="confirm_usage_speed_cameras">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.</string>
<string name="welcome_select_region">A jelzőtáblák és szabályok helyes értelmezéséhez jelölje ki a vezetési régiót:</string>
<string name="welcome_text">Az OsmAnd lehetővé teszi a térképek és a navigáció offline használatát az egész világon.</string>
<string name="current_route">Jelenlegi útvonal</string>
@ -3583,7 +3583,7 @@
<string name="uninstall_speed_cameras">Traffipaxok eltávolítása</string>
<string name="shared_string_legal">Jogi</string>
<string name="speed_camera_pois">Traffipax POI-k</string>
<string name="speed_cameras_legal_descr">Bizonyos országokban és régiókban a traffipaxra figyelmeztető alkalmazások törvényileg tiltottak.
<string name="speed_cameras_legal_descr">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,7 +3592,7 @@
\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.</string>
<string name="keep_active">Maradjanak</string>
<string name="shared_string_uninstall">Eltávolít</string>
<string name="speed_cameras_alert">Bizonyos országokban a traffipax riasztások törvényileg tiltottak.</string>
<string name="speed_cameras_alert">Bizonyos országokban a törvény tiltja a traffipaxriasztást.</string>
<string name="shared_string_bearing">Tájolás</string>
<string name="item_deleted">%1$s törölve</string>
<string name="speed_cameras_restart_descr">A traffipaxadatok végleges törléséhez indítsa újra az alkalmazást.</string>
@ -4044,7 +4044,11 @@
<string name="lost_data_warning">Minden nem mentett adat törlődni fog.</string>
<string name="show_start_dialog">Kezdő párbeszéd megjelenítése</string>
<string name="trip_recording_show_start_dialog_setting">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.</string>
<string name="release_4_0_beta">• Az OsmAnd Live frissítések átköltöztek a „Letöltések&gt; Frissítések” helyre
<string name="release_4_0_beta">• Ú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&gt; Frissítések” helyre
\n
\n• A nyomvonalak színezhetők magasság, sebesség vagy lejtés szerint.
\n
@ -4078,4 +4082,8 @@
<string name="announce_when_exceeded">Értesítés túllépéskor</string>
<string name="user_points">Felhasználói pontok</string>
<string name="output">Teljesítmény</string>
<string name="srtm_download_single_help_message">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.</string>
<string name="srtm_download_list_help_message">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.</string>
<string name="shared_string_feet">láb</string>
<string name="srtm_unit_format">Szintvonalak mértékegysége</string>
</resources>

View file

@ -3927,4 +3927,5 @@
<string name="poi_office_diplomatic">Sendiskrifstofa</string>
<string name="poi_bay_filter">Gerð flóa</string>
<string name="poi_plateau">Háslétta</string>
<string name="poi_club_social">Félagsstarf</string>
</resources>

View file

@ -4052,7 +4052,11 @@
<string name="rendering_attr_noNatureReserveBoundaries_name">Mörk náttúru</string>
<string name="trip_recording_logging_interval_info">Millibil skráninga stillir tímabilið milli þess sem OsmAnd biður um staðsetningargögn.</string>
<string name="trip_recording_show_start_dialog_setting">Ef þetta er óvirkt, mun upptaka hefjast strax eftir að ýtt er á hnappinn eða valmyndarfærsluna og staðfestingarglugga er þá sleppt.</string>
<string name="release_4_0_beta">• Uppfærslur OsmAnd Live færðar í \"Sótt gögn &gt; Uppfærslur\"
<string name="release_4_0_beta">• 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 &gt; Uppfærslur\"
\n
\n • Ferla er nú hægt að lita eftir hæð, hraða eða halla.
\n
@ -4091,4 +4095,8 @@
<string name="announce_when_exceeded">Tilkynna þegar farið er yfir</string>
<string name="user_points">Punktar notanda</string>
<string name="map_quick_action_pattern">%1$s → …</string>
<string name="shared_string_feet">fet</string>
<string name="srtm_unit_format">Snið eininga hæðarlína</string>
<string name="srtm_download_list_help_message">OsmAnd býður upp á hæðalínugögn í metrum og fetum. Þú þarft að sækja skrána aftur til að breyta sniðinu.</string>
<string name="srtm_download_single_help_message">Veldu rétt snið eininga. Þú þarft að sækja skrána aftur til að breyta sniðinu.</string>
</resources>

View file

@ -4056,7 +4056,11 @@
<string name="customize_route_line">להתאים קו מסלול אישית</string>
<string name="shared_string_route_line">קו מסלול</string>
<string name="specify_color_for_map_mode">לציין צבע למצב מפה: %1$s.</string>
<string name="release_4_0_beta">• העדכונים החיים של OsmAnd הועברו אל „הורדות &gt; עדכונים”
<string name="release_4_0_beta">• נוספה אפשרות להוריד קווי מתאר ברגל
\n
\n• תכנון מסלול אופקית: נוספו לשוניות למעבר בין נקודות לתרשימים
\n
\n• העדכונים החיים של OsmAnd הועברו אל „הורדות &gt; עדכונים”
\n
\n• אפשר לצבוע מסלולים לפי גובה, מהירות או שיפוע.
\n
@ -4087,4 +4091,8 @@
<string name="announce_when_exceeded">להכריז בחריגה</string>
<string name="user_points">נקודות משתמש</string>
<string name="output">פלט</string>
<string name="shared_string_feet">רגל</string>
<string name="srtm_unit_format">תצורת יחידת קווי מתאר</string>
<string name="srtm_download_list_help_message">OsmAnd מספק נתוני קווי מתאר במטרים וברגל. יהיה עליך להוריד את הקובץ מחדש כדי לשנות את התצורה.</string>
<string name="srtm_download_single_help_message">נא לבחור את התצורה הרצויה. יהיה עליך להוריד את הקובץ מחדש כדי לשנות את התצורה.</string>
</resources>

View file

@ -3843,7 +3843,7 @@ POIの更新は利用できません</string>
<string name="subscription_expired_title">OsmAndLiveサブスクリプションの有効期限が切れました</string>
<string name="subscription_paused_title">OsmAndLiveサブスクリプションが一時停止されました</string>
<string name="subscription_on_hold_title">OsmAndLiveサブスクリプションは保留中です</string>
<string name="markers_history">マーカー履歴</string>
<string name="markers_history">マーカー履歴</string>
<string name="send_files_to_openstreetmap">GPXファイルをOpenStreetMapに送信</string>
<string name="enter_text_separated">タグはカンマで区切って入力してください。</string>
<string name="gpx_upload_public_visibility_descr">\"公開 \"状態は、追跡機能にてユーザーのGPS追跡、公開GPS追跡リスト、および生データのタイムスタンプ付き公開追跡リストに公開されることを意味します。APIを介して提供されるデータはユーザーの追跡ページを参照しません。追跡ポイントのタイムスタンプはパブリックGPS APIでは利用できず、また追跡ポイントは時系列に並んでいません。</string>

View file

@ -3927,4 +3927,5 @@
<string name="poi_cliff_diving">Mergulho de falésia</string>
<string name="poi_zurkhaneh_sport">Zurkhaneh</string>
<string name="poi_bay_filter">Tipo de baía</string>
<string name="poi_ultimate">Ultimate</string>
</resources>

View file

@ -4049,7 +4049,11 @@
<string name="shared_string_route_line">Linha de rota</string>
<string name="route_line_use_map_style_appearance">A linha de rota seria usada %1$s especificado no estilo de mapa selecionado: %2$s.</string>
<string name="specify_color_for_map_mode">Especifique a cor para o modo de mapa: %1$s.</string>
<string name="release_4_0_beta">• As atualizações do OsmAnd Live foram movidas para \"Downloads &gt; Atualizações\"
<string name="release_4_0_beta">"• Adicionada opção para baixar curvas de nível em pés
\n
\n • Plano de paisagem da rota: guias adicionadas para alternar entre pontos ou gráficos
\n
\n • As atualizações do OsmAnd Live foram movidas para \"Downloads&gt; Atualizações\"
\n
\n • As trilhas agora podem ser coloridas por altitude, velocidade ou inclinação.
\n
@ -4057,7 +4061,7 @@
\n
\n • Caixa de diálogo \"Gravação de viagem\" atualizada
\n
\n</string>
\n"</string>
<string name="no_purchases">Você não tem nenhuma compra</string>
<string name="new_device_account">Novo dispositivo / nova conta</string>
<string name="contact_support_description">Se você tiver alguma dúvida, entre em contato conosco em %1$s.</string>
@ -4081,4 +4085,8 @@
<string name="user_points">Pontos do usuário</string>
<string name="output">Saída</string>
<string name="map_quick_action_pattern">%1$s → …</string>
<string name="shared_string_feet">pés</string>
<string name="srtm_unit_format">Formato da unidade de curvas de nível</string>
<string name="srtm_download_list_help_message">OsmAnd fornece dados de linhas de contorno em metros e pés. Você precisará baixar novamente o arquivo para alterar o formato.</string>
<string name="srtm_download_single_help_message">Selecione o formato necessário. Você precisará baixar novamente o arquivo para alterar o formato.</string>
</resources>

View file

@ -193,7 +193,7 @@
<string name="poi_ford_stepping_stones">Ponte pedonal em pedras separadas</string>
<string name="poi_mountain_pass">Passo de montanha</string>
<string name="poi_gate">Portão</string>
<string name="poi_city_wall">Muralha de cidade</string>
<string name="poi_city_wall">Muralha/cerca de cidade</string>
<string name="poi_lift_gate">Cancela elevatória</string>
<string name="poi_toll_booth">Cabine de portagem</string>
<string name="poi_border_control">Controlo aduaneiro</string>
@ -637,13 +637,13 @@
<string name="poi_australian_football">Futebol australiano</string>
<string name="poi_base">Base jumping</string>
<string name="poi_baseball">Beisebol</string>
<string name="poi_basketball">Basquete</string>
<string name="poi_basketball">Basquetebol</string>
<string name="poi_beachvolleyball">Voleibol de praia</string>
<string name="poi_bmx">BMX</string>
<string name="poi_boules">Bocha</string>
<string name="poi_bowls">Lawn bowls</string>
<string name="poi_canadian_football">Futebol canadiano</string>
<string name="poi_canoe">Canoa</string>
<string name="poi_canoe">Canoagem</string>
<string name="poi_chess">Xadrez</string>
<string name="poi_climbing">Escalada</string>
<string name="poi_cricket">Críquete</string>
@ -694,7 +694,7 @@
<string name="poi_boundary_stone">Marco de fronteira</string>
<string name="poi_historic_cannon">Canhão histórico</string>
<string name="poi_castle">Castelo</string>
<string name="poi_city_gate">Portão/arco de cidade</string>
<string name="poi_city_gate">Portão/porta/arco de cidade</string>
<string name="poi_fort">Forte</string>
<string name="poi_fountain">Chafariz</string>
<string name="poi_historic_ruins">Ruínas históricas</string>
@ -707,7 +707,7 @@
<string name="poi_aquarium">Aquário</string>
<string name="poi_theme_park">Parque de diversões</string>
<string name="poi_attraction">Atração turística</string>
<string name="poi_tourism_yes">Objeto turístico</string>
<string name="poi_tourism_yes">Elemento turístico</string>
<string name="poi_attraction_amusement_ride">Atracão de feira</string>
<string name="poi_attraction_animal">Animal (atração)</string>
<string name="poi_attraction_big_wheel">Roda gigante</string>
@ -2866,9 +2866,9 @@
<string name="poi_aquaculture_mussels">Aquicultura: mexilhões</string>
<string name="poi_mdf">Rede de distribuição principal (MDF)</string>
<string name="poi_min_age">Idade mínima</string>
<string name="poi_organic_yes">Sim</string>
<string name="poi_organic_no">Não</string>
<string name="poi_organic_only">Unicamente</string>
<string name="poi_organic_yes">Produtos orgânicos: sim</string>
<string name="poi_organic_no">Produtos orgânicos: não</string>
<string name="poi_organic_only">Produtos orgânicos: unicamente</string>
<string name="poi_traffic_mirror">Espelho de tráfego</string>
<string name="poi_diplomatic_consulate">Consulado</string>
<string name="poi_diplomatic_consulate_general">Consulado geral</string>
@ -3746,7 +3746,7 @@
<string name="poi_community_gender_male">Sexo comunitário: masculino</string>
<string name="poi_community_gender_mixed">Sexo comunitário: misto</string>
<string name="poi_grave">Sepultura</string>
<string name="poi_parking_space">Espaço de estacionamento</string>
<string name="poi_parking_space">Lugar de estacionamento (1 veículo)</string>
<string name="poi_url">URL</string>
<string name="poi_volcano_type">Tipo</string>
<string name="poi_volcano_status">Estado</string>

View file

@ -678,9 +678,9 @@
<string name="loading_builds">A carregar compilações OsmAnd…</string>
<string name="select_build_to_install">Selecionar a compilação OsmAnd a instalar</string>
<string name="contribution_activity">Instalar versão</string>
<string name="navigate_point_format_D">DDD.DDDDD</string>
<string name="navigate_point_format_DM">DDD MM.MMM</string>
<string name="navigate_point_format_DMS">DDD MM SS.S</string>
<string name="navigate_point_format_D">GGG.GGGGG</string>
<string name="navigate_point_format_DM">GGG MM.MMM</string>
<string name="navigate_point_format_DMS">GGG MM SS.S</string>
<string name="rendering_attr_noPolygons_description">Tornar transparentes todas as características do terreno no mapa.</string>
<string name="rendering_attr_noPolygons_name">Polígonos</string>
<string name="rendering_attr_appMode_name">Modo de visualização</string>
@ -1021,7 +1021,7 @@
<string name="av_camera_focus_infinity">Focar infinito</string>
<string name="av_camera_focus_macro">Focagem macro (close-up)</string>
<string name="av_camera_focus_continuous">A câmara tenta focar continuadamente</string>
<string name="av_photo_play_sound">Reproduzir o som do obturador da câmara</string>
<string name="av_photo_play_sound">Reproduzir som ao tirar fotografias</string>
<string name="av_photo_play_sound_descr">Definir som ou silêncio ao fotografar.</string>
<string name="driving_region_canada">Canadá</string>
<string name="about_version">Versão:</string>
@ -2013,7 +2013,7 @@
<string name="routing_attr_height_obstacles_name">Utilizar dados de elevação</string>
<string name="rendering_attr_depthContours_description">Mostrar pontos e contornos de profundidade.</string>
<string name="rendering_attr_depthContours_name">Contornos de profundidade náuticos</string>
<string name="show_transparency_seekbar">Mostra a transparência da barra de navegação</string>
<string name="show_transparency_seekbar">Mostrar barra deslizante de transparência</string>
<string name="shared_string_widgets">Widgets</string>
<string name="rendering_attr_hideUnderground_name">Objetos subterrâneos</string>
<string name="auto_split_recording_title">Dividir automaticamente as gravações após quebras</string>
@ -3440,7 +3440,7 @@
<string name="create_custom_categories_list_promo">Altere a ordenação da lista e oculte categorias. Pode importar ou exportar todas as alterações com perfis.</string>
<string name="rearrange_categories">Reorganizar categorias</string>
<string name="osm_authorization_success">Autorização bem sucedida</string>
<string name="multimedia_photo_play_sound">Som do obturador da câmara</string>
<string name="multimedia_photo_play_sound">Som ao tirar fotografias</string>
<string name="multimedia_use_system_camera">Usar aplicação do sistema</string>
<string name="multimedia_rec_split_title">Dividir gravações</string>
<string name="reset_plugin_to_default">Repor configurações originais da extensão</string>
@ -3960,13 +3960,17 @@
<string name="routing_engine_vehicle_type_cycling_electric">Bicicleta elétrica</string>
<string name="live_update_frequency_hour_variant">As atualizações ao mapa serão verificadas a todas as horas. Próxima %1$s em %2$s.</string>
<string name="live_update_delete_updates_msg">Tem a certeza que quer eliminar todas as %s atualizações OsmAnd Live\?</string>
<string name="release_4_0_beta">• Atualizações OsmAnd Live movidas para \"Descarregamentos &gt; Atualizações\"
<string name="release_4_0_beta">• Adicionada opção para descarregar curvas de nível em pés.
\n
\n• Planear rota: adicionadas abas para alternar entre pontos ou gráficos.
\n
\n• Atualizações OsmAnd Live movidas para \"Descarregamentos &gt; Atualizações\"
\n
\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• 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• Janela de diálogo \"Gravação do trilho\" atualizada.
\n
\n</string>
<string name="routing_attr_height_obstacles_description">O roteamento pode evitar subidas íngremes.</string>
@ -4089,4 +4093,8 @@
<string name="announce_when_exceeded">Anunciar quando ultrapassado</string>
<string name="user_points">Pontos do utilizador</string>
<string name="output">Saída</string>
<string name="shared_string_feet">pés</string>
<string name="srtm_download_list_help_message">O OsmAnd fornece dados de curvas de nível em metros e pés. Terá de descarregar novamente o ficheiro para alterar o formato.</string>
<string name="srtm_unit_format">Formato de unidades de curvas de nível</string>
<string name="srtm_download_single_help_message">Selecione o formato necessário. Terá de descarregar novamente o ficheiro para alterar o formato.</string>
</resources>

View file

@ -2872,7 +2872,7 @@
<string name="sit_on_the_stop">Посадка на остановке</string>
<string name="use_osm_live_public_transport_description">Включить общественный транспорт с учётом автообновлений OsmAnd Live.</string>
<string name="use_osm_live_public_transport">Общественный транспорт OsmAnd Live</string>
<string name="transfers_size">%1$d пересадки</string>
<string name="transfers_size">пересадки: %1$d</string>
<string name="rendering_attr_surface_unpaved_name">Грунтовая</string>
<string name="rendering_attr_surface_sand_name">Песок</string>
<string name="rendering_attr_surface_grass_name">Трава</string>
@ -3263,7 +3263,7 @@
<string name="selected_profile">Выбранный профиль</string>
<string name="personal_category_name">Персональный</string>
<string name="shared_string_downloading_formatted">Скачивание %s</string>
<string name="rendering_value_thick_name">Толсто</string>
<string name="rendering_value_thick_name">Толстая</string>
<string name="default_speed_dialog_msg">Используется для оценки времени прибытия для неизвестного типа дорог и ограничения скорости для всех дорог (может изменить маршрут)</string>
<string name="routing_attr_allow_intermediate_name">Разрешить промежуточные маршруты</string>
<string name="routing_attr_allow_advanced_name">Разрешить расширенные маршруты</string>
@ -3302,7 +3302,7 @@
<string name="rendering_attr_piste_difficulty_connection_name">Соединение</string>
<string name="simulate_your_location_gpx_descr">Симулировать свою позицию используя записанный GPX трек.</string>
<string name="route_start_point">Начало маршрута</string>
<string name="shared_string_revert">Вернуться</string>
<string name="shared_string_revert">Сброс</string>
<string name="suggested_maps_descr">Эти карты необходимо использовать с плагином.</string>
<string name="added_profiles">Добавленные профили</string>
<string name="added_profiles_descr">Профили, добавленные плагином</string>
@ -4056,7 +4056,11 @@
<string name="next_billing_date">Следующая дата оплаты: %1$s</string>
<string name="osmand_live">OsmAnd Live</string>
<string name="annual_subscription">Годовая подписка</string>
<string name="release_4_0_beta">• Обновления OsmAnd Live перемещены в «Загрузка карт» → «Обновления».
<string name="release_4_0_beta">• Добавлена возможность скачать контурные линии в футах.
\n
\n• Планирование маршрута: добавлены вкладки для переключения между точками и графиками.
\n
\n• Обновления OsmAnd Live перемещены в «Загрузка карт» → «Обновления».
\n
\n• Теперь треки можно раскрашивать по высоте, скорости или уклону.
\n
@ -4087,4 +4091,8 @@
<string name="output">Вывод</string>
<string name="map_quick_action_pattern">%1$s → …</string>
<string name="exit_number">Номер съезда</string>
<string name="srtm_unit_format">Формат единиц на контурных линиях</string>
<string name="srtm_download_single_help_message">Выберите необходимый формат. Для изменения формата потребуется повторно загрузить файл.</string>
<string name="shared_string_feet">футы</string>
<string name="srtm_download_list_help_message">OsmAnd предоставляет данные изолиний в метрах и футах. Вам нужно будет повторно загрузить файл, чтобы изменить формат.</string>
</resources>

View file

@ -4045,7 +4045,11 @@
<string name="lost_data_warning">Všetky neuložené údaje budú stratené.</string>
<string name="show_start_dialog">Zobraziť úvodné okno</string>
<string name="trip_recording_show_start_dialog_setting">Ak je vypnuté, záznam začne hneď po stlačení nástroja alebo položky v menu a preskočí okno nastavenia.</string>
<string name="release_4_0_beta">• Aktualizácie OsmAnd Live presunuté do \"Sťahovania &gt; Aktualizácie\"
<string name="release_4_0_beta">• Pridaná možnosť stiahnutia Vrstevníc v stopách
\n
\n• Plánovať trasy: pridané prepínače medzi bodmi a grafmi
\n
\n• Aktualizácie OsmAnd Live presunuté do \"Sťahovania &gt; Aktualizácie\"
\n
\n • Stopy je teraz možné vyfarbiť podľa nadmorskej výšky, rýchlosti alebo sklonu svahu.
\n
@ -4081,4 +4085,8 @@
<string name="user_points">Body používateľa</string>
<string name="output">Výstup</string>
<string name="map_quick_action_pattern">%1$s → …</string>
<string name="shared_string_feet">stopy</string>
<string name="srtm_unit_format">Formát jednotiek vrstevníc</string>
<string name="srtm_download_list_help_message">OsmAnd poskytuje údaje vrstevníc v metroch a stopách. Budete musieť znovu stiahnuť súbor pre zmenu formátu.</string>
<string name="srtm_download_single_help_message">Prosím zvoľte požadovaný formát. Budete musieť znovu stiahnuť súbor pre zmenu formátu.</string>
</resources>

View file

@ -4072,7 +4072,11 @@
<string name="in_grace_period">Ek süre içinde</string>
<string name="on_hold">Beklemede</string>
<string name="expired">Süresi doldu</string>
<string name="release_4_0_beta">• OsmAnd Live güncellemeleri \"İndirmeler&gt; Güncellemeler\" bölümüne taşındı
<string name="release_4_0_beta">• 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 &gt; 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 @@
<string name="user_points">Kullanıcı puanları</string>
<string name="output">Çıkış</string>
<string name="map_quick_action_pattern">%1$s → …</string>
<string name="shared_string_feet">fit</string>
<string name="srtm_unit_format">Eş yükselti eğrileri birimi biçimi</string>
<string name="srtm_download_list_help_message">OsmAnd, metre ve fit cinsinden eş yükselti eğrileri verileri sağlar. Biçimi değiştirmek için dosyayı yeniden indirmeniz gerekecek.</string>
<string name="srtm_download_single_help_message">Lütfen gerekli biçimi seçin. Biçimi değiştirmek için dosyayı yeniden indirmeniz gerekecek.</string>
</resources>

View file

@ -4053,7 +4053,11 @@
<string name="shared_string_route_line">Лінія маршруту</string>
<string name="route_line_use_map_style_appearance">Лінія маршруту застосовуватиме %1$s, вказаний у вибраному стилі мапи: %2$s.</string>
<string name="specify_color_for_map_mode">Вкажіть колір для режиму мапи: %1$s.</string>
<string name="release_4_0_beta">• Оновлення OsmAnd Live переміщено до «Завантаження &gt;Оновлення»
<string name="release_4_0_beta">• Додана можливість завантаження контурних ліній у футах
\n
\n• Ландшафтне планування маршруту: додані вкладки для перемикання між точками або графіками
\n
\n• Оновлення OsmAnd Live переміщено до «Завантаження &gt;Оновлення»
\n
\n• Тепер треки можуть бути забарвлені за висотою, швидкістю або нахилом.
\n
@ -4084,4 +4088,8 @@
<string name="announce_when_exceeded">Повідомляти про перевищення</string>
<string name="user_points">Користувацькі точки</string>
<string name="output">Вивід</string>
<string name="srtm_download_list_help_message">OsmAnd надає дані горизонталей в метрах і футах. Щоб змінити формат, потрібно повторно завантажити файл.</string>
<string name="srtm_unit_format">Формат одиниць вимірювання горизонталей</string>
<string name="shared_string_feet">фути</string>
<string name="srtm_download_single_help_message">Виберіть потрібний формат. Щоб змінити формат, потрібно повторно завантажувати файл.</string>
</resources>

View file

@ -4047,7 +4047,11 @@
<string name="shared_string_route_line">路線</string>
<string name="route_line_use_map_style_appearance">路線將會使用 %1$s 在選定的地圖樣式上指定的:%2$s。</string>
<string name="specify_color_for_map_mode">指定地圖模式的顏色:%1$s。</string>
<string name="release_4_0_beta">• OsmAnd Live 更新移動至「下載 &gt; 更新」
<string name="release_4_0_beta">• 新增以英呎為單位下載等高線
\n
\n • 規劃路線樣式:新增切換點與圖形的分頁
\n
\n • OsmAnd Live 更新移動至「下載 &gt; 更新」
\n
\n • 軌跡現在可以使用海拔、速度或坡度來填色
\n
@ -4079,4 +4083,8 @@
<string name="user_points">使用者點</string>
<string name="output">輸出</string>
<string name="map_quick_action_pattern">%1$s → …</string>
<string name="shared_string_feet">英呎</string>
<string name="srtm_unit_format">等高線單位格式</string>
<string name="srtm_download_list_help_message">OsmAnd 提供以公尺與英呎為單位的等高線資料。您將必須重新下載檔案以變更格式。</string>
<string name="srtm_download_single_help_message">請選取需要格式。您將必須重新下載檔案以變更格式。</string>
</resources>

View file

@ -12,6 +12,9 @@
-->
<string name="shared_string_max_height">Max. height</string>
<string name="shared_string_min_height">Min. height</string>
<string name="route_line_use_gradient_coloring">Route line will be colorized depending on the elevation profile of the route.</string>
<string name="output">Output</string>
<string name="user_points">User points</string>
<string name="announce_when_exceeded">Announce when exceeded</string>

View file

@ -38,6 +38,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.Executor;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
@ -47,13 +48,14 @@ public class AndroidNetworkUtils {
private static final Log LOG = PlatformUtil.getLog(AndroidNetworkUtils.class);
public interface OnRequestResultListener {
void onResult(String result);
void onResult(@Nullable String result, @Nullable String error);
}
public interface OnFilesUploadCallback {
@Nullable
Map<String, String> getAdditionalParams(@NonNull File file);
void onFileUploadProgress(@NonNull File file, int percent);
void onFileUploadDone(@NonNull File file);
void onFilesUploadDone(@NonNull Map<File, String> errors);
}
@ -63,16 +65,26 @@ public class AndroidNetworkUtils {
void onFileDownloadProgress(@NonNull File file, int percent);
@WorkerThread
void onFileDownloadedAsync(@NonNull File file);
void onFileDownloadDone(@NonNull File file);
void onFilesDownloadDone(@NonNull Map<File, String> errors);
}
public static class RequestResponse {
private Request request;
private String response;
private final Request request;
private final String response;
private final String error;
RequestResponse(@NonNull Request request, @Nullable String response) {
this.request = request;
this.response = response;
this.error = null;
}
RequestResponse(@NonNull Request request, @Nullable String response, @Nullable String error) {
this.request = request;
this.response = response;
this.error = error;
}
public Request getRequest() {
@ -82,6 +94,10 @@ public class AndroidNetworkUtils {
public String getResponse() {
return response;
}
public String getError() {
return error;
}
}
public interface OnSendRequestsListener {
@ -92,6 +108,13 @@ public class AndroidNetworkUtils {
public static void sendRequestsAsync(@Nullable final OsmandApplication ctx,
@NonNull final List<Request> requests,
@Nullable final OnSendRequestsListener listener) {
sendRequestsAsync(ctx, requests, listener, AsyncTask.THREAD_POOL_EXECUTOR);
}
public static void sendRequestsAsync(@Nullable final OsmandApplication ctx,
@NonNull final List<Request> requests,
@Nullable final OnSendRequestsListener listener,
final Executor executor) {
new AsyncTask<Void, RequestResponse, List<RequestResponse>>() {
@ -101,11 +124,18 @@ public class AndroidNetworkUtils {
for (Request request : requests) {
RequestResponse requestResponse;
try {
String response = sendRequest(ctx, request.getUrl(), request.getParameters(),
request.getUserOperation(), request.isToastAllowed(), request.isPost());
requestResponse = new RequestResponse(request, response);
final String[] response = {null, null};
sendRequest(ctx, request.getUrl(), request.getParameters(),
request.getUserOperation(), request.isToastAllowed(), request.isPost(), new OnRequestResultListener() {
@Override
public void onResult(@Nullable String result, @Nullable String error) {
response[0] = result;
response[1] = error;
}
});
requestResponse = new RequestResponse(request, response[0], response[1]);
} catch (Exception e) {
requestResponse = new RequestResponse(request, null);
requestResponse = new RequestResponse(request, null, "Unexpected error");
}
responses.add(requestResponse);
publishProgress(requestResponse);
@ -127,7 +157,7 @@ public class AndroidNetworkUtils {
}
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null);
}.executeOnExecutor(executor, (Void) null);
}
public static void sendRequestAsync(final OsmandApplication ctx,
@ -137,26 +167,45 @@ public class AndroidNetworkUtils {
final boolean toastAllowed,
final boolean post,
final OnRequestResultListener listener) {
sendRequestAsync(ctx, url, parameters, userOperation, toastAllowed, post, listener,
AsyncTask.THREAD_POOL_EXECUTOR);
}
new AsyncTask<Void, Void, String>() {
public static void sendRequestAsync(final OsmandApplication ctx,
final String url,
final Map<String, String> parameters,
final String userOperation,
final boolean toastAllowed,
final boolean post,
final OnRequestResultListener listener,
final Executor executor) {
new AsyncTask<Void, Void, String[]>() {
@Override
protected String doInBackground(Void... params) {
protected String[] doInBackground(Void... params) {
final String[] res = {null, null};
try {
return sendRequest(ctx, url, parameters, userOperation, toastAllowed, post);
} catch (Exception e) {
return null;
sendRequest(ctx, url, parameters, userOperation, toastAllowed, post, new OnRequestResultListener() {
@Override
public void onResult(@Nullable String result, @Nullable String error) {
res[0] = result;
res[1] = error;
}
});
} catch (Exception e) {
// ignore
}
return res;
}
@Override
protected void onPostExecute(String response) {
protected void onPostExecute(String[] response) {
if (listener != null) {
listener.onResult(response);
listener.onResult(response[0], response[1]);
}
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null);
}.executeOnExecutor(executor, (Void) null);
}
public static void downloadFileAsync(final String url,
@ -183,6 +232,14 @@ public class AndroidNetworkUtils {
final @NonNull List<File> files,
final @NonNull Map<String, String> parameters,
final @Nullable OnFilesDownloadCallback callback) {
downloadFilesAsync(url, files, parameters, callback, AsyncTask.THREAD_POOL_EXECUTOR);
}
public static void downloadFilesAsync(final @NonNull String url,
final @NonNull List<File> files,
final @NonNull Map<String, String> parameters,
final @Nullable OnFilesDownloadCallback callback,
final Executor executor) {
new AsyncTask<Void, Object, Map<File, String>>() {
@ -228,7 +285,7 @@ public class AndroidNetworkUtils {
} catch (Exception e) {
errors.put(file, e.getMessage());
}
publishProgress(file, Integer.MAX_VALUE);
publishProgress(file, -1);
}
return errors;
}
@ -236,7 +293,13 @@ public class AndroidNetworkUtils {
@Override
protected void onProgressUpdate(Object... objects) {
if (callback != null) {
callback.onFileDownloadProgress((File) objects[0], (Integer) objects[1]);
File file = (File) objects[0];
Integer progress = (Integer) objects[1];
if (progress >= 0) {
callback.onFileDownloadProgress(file, progress);
} else {
callback.onFileDownloadDone(file);
}
}
}
@ -247,15 +310,23 @@ public class AndroidNetworkUtils {
}
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null);
}.executeOnExecutor(executor, (Void) null);
}
public static String sendRequest(@Nullable OsmandApplication ctx, @NonNull String url,
@Nullable Map<String, String> parameters,
@Nullable String userOperation, boolean toastAllowed, boolean post) {
return sendRequest(ctx, url, parameters, userOperation, toastAllowed, post, null);
}
public static String sendRequest(@Nullable OsmandApplication ctx, @NonNull String url,
@Nullable Map<String, String> parameters,
@Nullable String userOperation, boolean toastAllowed, boolean post,
@Nullable OnRequestResultListener listener) {
String result = null;
String error = null;
HttpURLConnection connection = null;
try {
String params = null;
if (parameters != null && parameters.size() > 0) {
StringBuilder sb = new StringBuilder();
@ -285,68 +356,66 @@ public class AndroidNetworkUtils {
output.write(params.getBytes("UTF-8"));
output.flush();
output.close();
} else {
connection.setRequestMethod("GET");
connection.connect();
}
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
if (ctx != null) {
error = (!Algorithms.isEmpty(userOperation) ? userOperation + " " : "")
+ ctx.getString(R.string.failed_op) + ": " + connection.getResponseMessage();
} else {
error = (!Algorithms.isEmpty(userOperation) ? userOperation + " " : "")
+ "failed: " + connection.getResponseMessage();
}
if (toastAllowed && ctx != null) {
String msg = (!Algorithms.isEmpty(userOperation) ? userOperation + " " : "")
+ ctx.getString(R.string.failed_op) + ": "
+ connection.getResponseMessage();
showToast(ctx, msg);
showToast(ctx, error);
}
InputStream errorStream = connection.getErrorStream();
if (errorStream != null) {
error = streamToString(errorStream);
}
} else {
StringBuilder responseBody = new StringBuilder();
responseBody.setLength(0);
InputStream i = connection.getInputStream();
if (i != null) {
BufferedReader in = new BufferedReader(new InputStreamReader(i, "UTF-8"), 256);
String s;
boolean f = true;
while ((s = in.readLine()) != null) {
if (!f) {
responseBody.append("\n");
} else {
f = false;
result = streamToString(connection.getInputStream());
}
responseBody.append(s);
}
try {
in.close();
i.close();
} catch (Exception e) {
// ignore exception
}
}
return responseBody.toString();
}
} catch (NullPointerException e) {
// that's tricky case why NPE is thrown to fix that problem httpClient could be used
if (ctx != null) {
error = ctx.getString(R.string.auth_failed);
} else {
error = "Authorization failed";
}
if (toastAllowed && ctx != null) {
String msg = ctx.getString(R.string.auth_failed);
showToast(ctx, msg);
showToast(ctx, error);
}
} catch (MalformedURLException e) {
if (ctx != null) {
error = MessageFormat.format(ctx.getResources().getString(R.string.shared_string_action_template)
+ ": " + ctx.getResources().getString(R.string.shared_string_unexpected_error), userOperation);
} else {
error = "Action " + userOperation + ": Unexpected error";
}
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));
showToast(ctx, error);
}
} catch (IOException e) {
if (ctx != null) {
error = MessageFormat.format(ctx.getResources().getString(R.string.shared_string_action_template)
+ ": " + ctx.getResources().getString(R.string.shared_string_io_error), userOperation);
} else {
error = "Action " + userOperation + ": I/O error";
}
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));
showToast(ctx, error);
}
} finally {
if (connection != null) {
connection.disconnect();
}
}
if (listener != null) {
listener.onResult(result, error);
}
return null;
}
@ -374,12 +443,16 @@ public class AndroidNetworkUtils {
public static String downloadFile(@NonNull String url, @NonNull File fileToSave, boolean gzip, @Nullable IProgress progress) {
String error = null;
try {
URLConnection connection = NetworkUtils.getHttpURLConnection(url);
HttpURLConnection connection = NetworkUtils.getHttpURLConnection(url);
connection.setConnectTimeout(CONNECTION_TIMEOUT);
connection.setReadTimeout(CONNECTION_TIMEOUT);
if (gzip) {
connection.setRequestProperty("Accept-Encoding", "deflate, gzip");
}
connection.connect();
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
return streamToString(connection.getErrorStream());
} else {
InputStream inputStream = gzip
? new GZIPInputStream(connection.getInputStream())
: new BufferedInputStream(connection.getInputStream(), 8 * 1024);
@ -393,6 +466,7 @@ public class AndroidNetworkUtils {
Algorithms.closeStream(inputStream);
Algorithms.closeStream(stream);
}
}
} catch (UnknownHostException e) {
error = e.getMessage();
LOG.error("UnknownHostException, cannot download file " + url + " " + error);
@ -403,6 +477,30 @@ public class AndroidNetworkUtils {
return error;
}
private static String streamToString(InputStream inputStream) throws IOException {
StringBuilder result = new StringBuilder();
if (inputStream != null) {
BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"), 256);
String buffer;
boolean f = true;
while ((buffer = in.readLine()) != null) {
if (!f) {
result.append("\n");
} else {
f = false;
}
result.append(buffer);
}
try {
in.close();
inputStream.close();
} catch (Exception e) {
// ignore exception
}
}
return result.toString();
}
private static final String BOUNDARY = "CowMooCowMooCowCowCow";
public static String uploadFile(@NonNull String urlText, @NonNull File file, boolean gzip,
@ -417,7 +515,6 @@ public class AndroidNetworkUtils {
@NonNull Map<String, String> additionalParams,
@Nullable Map<String, String> headers,
@Nullable IProgress progress) {
URL url;
try {
boolean firstPrm = !urlText.contains("?");
StringBuilder sb = new StringBuilder(urlText);
@ -428,7 +525,7 @@ public class AndroidNetworkUtils {
urlText = sb.toString();
LOG.info("Start uploading file to " + urlText + " " + fileName);
url = new URL(urlText);
URL url = new URL(urlText);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setDoInput(true);
@ -469,6 +566,10 @@ public class AndroidNetworkUtils {
LOG.info("Finish uploading file " + fileName);
LOG.info("Response code and message : " + conn.getResponseCode() + " " + conn.getResponseMessage());
if (conn.getResponseCode() != 200) {
InputStream errorStream = conn.getErrorStream();
if (errorStream != null) {
return streamToString(errorStream);
}
return conn.getResponseMessage();
}
InputStream is = conn.getInputStream();
@ -503,6 +604,16 @@ public class AndroidNetworkUtils {
final @NonNull Map<String, String> parameters,
final @Nullable Map<String, String> headers,
final OnFilesUploadCallback callback) {
uploadFilesAsync(url, files, gzip, parameters, headers, callback, AsyncTask.THREAD_POOL_EXECUTOR);
}
public static void uploadFilesAsync(final @NonNull String url,
final @NonNull List<File> files,
final boolean gzip,
final @NonNull Map<String, String> parameters,
final @Nullable Map<String, String> headers,
final OnFilesUploadCallback callback,
final Executor executor) {
new AsyncTask<Void, Object, Map<File, String>>() {
@ -538,7 +649,7 @@ public class AndroidNetworkUtils {
} catch (Exception e) {
errors.put(file, e.getMessage());
}
publishProgress(file, Integer.MAX_VALUE);
publishProgress(file, -1);
}
return errors;
}
@ -546,7 +657,13 @@ public class AndroidNetworkUtils {
@Override
protected void onProgressUpdate(Object... objects) {
if (callback != null) {
callback.onFileUploadProgress((File) objects[0], (Integer) objects[1]);
File file = (File) objects[0];
Integer progress = (Integer) objects[1];
if (progress >= 0) {
callback.onFileUploadProgress(file, progress);
} else {
callback.onFileUploadDone(file);
}
}
}
@ -557,7 +674,7 @@ public class AndroidNetworkUtils {
}
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null);
}.executeOnExecutor(executor, (Void) null);
}
private static void showToast(OsmandApplication ctx, String message) {
@ -565,11 +682,11 @@ public class AndroidNetworkUtils {
}
public static class Request {
private String url;
private Map<String, String> parameters;
private String userOperation;
private boolean toastAllowed;
private boolean post;
private final String url;
private final Map<String, String> parameters;
private final String userOperation;
private final boolean toastAllowed;
private final boolean post;
public Request(String url, Map<String, String> parameters, String userOperation, boolean toastAllowed, boolean post) {
this.url = url;

View file

@ -40,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;
@ -473,7 +472,6 @@ 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();
@ -687,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();

View file

@ -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) {

View file

@ -225,7 +225,7 @@ public class CustomRegion extends WorldRegion {
&& app.getSettings().isInternetConnectionAvailable()) {
OnRequestResultListener resultListener = new OnRequestResultListener() {
@Override
public void onResult(String result) {
public void onResult(@Nullable String result, @Nullable String error) {
if (!Algorithms.isEmpty(result)) {
if ("json".equalsIgnoreCase(dynamicDownloadItems.format)) {
dynamicItemsJson = mapJsonItems(result);

View file

@ -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;
@ -280,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;
@ -297,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() {

View file

@ -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;
@ -797,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);
}
@ -824,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);
}
}

View file

@ -67,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;
@ -169,7 +168,6 @@ public class OsmandApplication extends MultiDexApplication {
OprAuthHelper oprAuthHelper;
MeasurementEditingContext measurementEditingContext;
OnlineRoutingHelper onlineRoutingHelper;
ItineraryHelper itineraryHelper;
BackupHelper backupHelper;
private Map<String, Builder> customRoutingConfigs = new ConcurrentHashMap<>();
@ -472,10 +470,6 @@ public class OsmandApplication extends MultiDexApplication {
return onlineRoutingHelper;
}
public ItineraryHelper getItineraryHelper() {
return itineraryHelper;
}
public BackupHelper getBackupHelper() {
return backupHelper;
}

View file

@ -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());

View file

@ -526,7 +526,7 @@ public class FavoritesTreeFragment extends OsmandExpandableListFragment implemen
for (Map.Entry<String, Set<FavouritePoint>> 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()));

View file

@ -37,6 +37,10 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class BackupHelper {
@ -45,6 +49,9 @@ public class BackupHelper {
private final FavouritesDbHelper favouritesHelper;
private final GpxDbHelper gpxHelper;
private static final ThreadPoolExecutor EXECUTOR = new ThreadPoolExecutor(1, 1, 0L,
TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
private static final String SERVER_URL = "https://osmand.net";
private static final String USER_REGISTER_URL = SERVER_URL + "/userdata/user-register";
@ -59,10 +66,6 @@ public class BackupHelper {
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);
}
@ -77,7 +80,6 @@ public class BackupHelper {
public interface OnCollectLocalFilesListener {
void onFileCollected(@NonNull GpxFileInfo fileInfo);
void onFilesCollected(@NonNull List<GpxFileInfo> fileInfos);
}
@ -87,20 +89,21 @@ public class BackupHelper {
public interface OnUploadFilesListener {
void onFileUploadProgress(@NonNull File file, int progress);
void onFileUploadDone(@NonNull File file);
void onFilesUploadDone(@NonNull Map<File, String> errors);
}
public interface OnDeleteFilesListener {
void onFileDeleteProgress(@NonNull UserFile file);
void onFilesDeleteDone(@NonNull Map<UserFile, String> errors);
}
public interface OnDownloadFileListener {
void onFileDownloadProgress(@NonNull UserFile userFile, int progress);
@WorkerThread
void onFileDownloadedAsync(@NonNull File file);
void onFileDownloaded(@NonNull File file);
void onFilesDownloadDone(@NonNull Map<File, String> errors);
}
@ -131,10 +134,6 @@ public class BackupHelper {
return token.matches("[0-9]+");
}
public boolean hasOsmLiveUpdates() {
return InAppPurchaseHelper.isSubscribedToLiveUpdates(app);
}
@Nullable
public String getOrderId() {
InAppPurchaseHelper purchaseHelper = app.getInAppPurchaseHelper();
@ -167,22 +166,27 @@ public class BackupHelper {
public void registerUser(@NonNull String email, @Nullable final OnRegisterUserListener listener) {
Map<String, String> params = new HashMap<>();
params.put("email", email);
params.put("orderid", getOrderId());
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() {
AndroidNetworkUtils.sendRequestAsync(app, USER_REGISTER_URL, params, "Register user", false, true, new OnRequestResultListener() {
@Override
public void onResult(String resultJson) {
public void onResult(@Nullable String resultJson, @Nullable String error) {
int status;
String message;
if (!Algorithms.isEmpty(resultJson)) {
if (!Algorithms.isEmpty(error)) {
message = "User registration error: " + parseServerError(error);
status = STATUS_SERVER_ERROR;
} else if (!Algorithms.isEmpty(resultJson)) {
try {
JSONObject result = new JSONObject(resultJson);
String statusStr = result.getString("status");
if (statusStr.equals("ok")) {
if (result.has("status") && "ok".equals(result.getString("status"))) {
message = "You have been registered successfully. Please check for email with activation code.";
status = STATUS_SUCCESS;
} else {
message = "User registration error: " + statusStr;
message = "User registration error: unknown";
status = STATUS_SERVER_ERROR;
}
} catch (JSONException e) {
@ -197,7 +201,7 @@ public class BackupHelper {
listener.onRegisterUser(status, message);
}
}
});
}, EXECUTOR);
}
public void registerDevice(String token, @Nullable final OnRegisterDeviceListener listener) {
@ -212,12 +216,15 @@ public class BackupHelper {
params.put("deviceid", androidId);
}
params.put("token", token);
AndroidNetworkUtils.sendRequestAsync(app, DEVICE_REGISTER_URL, params, "Register device", true, true, new OnRequestResultListener() {
AndroidNetworkUtils.sendRequestAsync(app, DEVICE_REGISTER_URL, params, "Register device", false, true, new OnRequestResultListener() {
@Override
public void onResult(String resultJson) {
public void onResult(@Nullable String resultJson, @Nullable String error) {
int status;
String message;
if (!Algorithms.isEmpty(resultJson)) {
if (!Algorithms.isEmpty(error)) {
message = "Device registration error: " + parseServerError(error);
status = STATUS_SERVER_ERROR;
} else if (!Algorithms.isEmpty(resultJson)) {
try {
JSONObject result = new JSONObject(resultJson);
settings.BACKUP_DEVICE_ID.set(result.getString("id"));
@ -225,8 +232,9 @@ public class BackupHelper {
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";
status = STATUS_SUCCESS;
} catch (JSONException e) {
message = "Device registration error: json parsing";
status = STATUS_PARSE_JSON_ERROR;
@ -239,7 +247,7 @@ public class BackupHelper {
listener.onRegisterDevice(status, message);
}
}
});
}, EXECUTOR);
}
public void uploadFiles(@NonNull List<GpxFileInfo> gpxFiles, @Nullable final OnUploadFilesListener listener) throws UserNotRegisteredException {
@ -266,14 +274,6 @@ public class BackupHelper {
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;
@ -286,16 +286,34 @@ public class BackupHelper {
}
}
@Override
public void onFileUploadDone(@NonNull File file) {
if (listener != null) {
GpxFileInfo gpxFileInfo = gpxInfos.get(file);
if (gpxFileInfo != null) {
if (file.equals(favoritesFile)) {
favouritesHelper.setLastUploadedTime(gpxFileInfo.uploadTime);
} else {
GpxDataItem gpxItem = gpxHelper.getItem(file);
if (gpxItem != null) {
gpxHelper.updateLastUploadedTime(gpxItem, gpxFileInfo.uploadTime);
}
}
}
listener.onFileUploadDone(file);
}
}
@Override
public void onFilesUploadDone(@NonNull Map<File, String> errors) {
if (errors.isEmpty()) {
settings.BACKUP_LAST_UPLOADED_TIME.set(System.currentTimeMillis() + 1);
}
if (listener != null) {
listener.onFilesUploadDone(errors);
listener.onFilesUploadDone(resolveServerErrors(errors));
}
}
});
}, EXECUTOR);
}
public void deleteFiles(@NonNull List<UserFile> userFiles, @Nullable final OnDeleteFilesListener listener) throws UserNotRegisteredException {
@ -333,24 +351,36 @@ public class BackupHelper {
for (RequestResponse response : results) {
UserFile userFile = filesMap.get(response.getRequest());
if (userFile != null) {
String responseStr = response.getResponse();
boolean success;
String message = null;
String errorStr = response.getError();
if (!Algorithms.isEmpty(errorStr)) {
message = parseServerError(errorStr);
success = false;
} else {
String responseStr = response.getResponse();
try {
JSONObject json = new JSONObject(responseStr);
String status = json.getString("status");
success = status.equalsIgnoreCase("ok");
} catch (JSONException e) {
JSONObject result = new JSONObject(responseStr);
if (result.has("status") && "ok".equals(result.getString("status"))) {
success = true;
} else {
message = "Unknown error";
success = false;
}
} catch (JSONException e) {
message = "Json parsing error";
success = false;
}
}
if (!success) {
errors.put(userFile, responseStr);
errors.put(userFile, message);
}
}
}
listener.onFilesDeleteDone(errors);
}
}
});
}, EXECUTOR);
}
public void downloadFileList(@Nullable final OnDownloadFileListListener listener) throws UserNotRegisteredException {
@ -359,13 +389,16 @@ public class BackupHelper {
Map<String, String> 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() {
AndroidNetworkUtils.sendRequestAsync(app, LIST_FILES_URL, params, "Download file list", false, false, new OnRequestResultListener() {
@Override
public void onResult(String resultJson) {
public void onResult(@Nullable String resultJson, @Nullable String error) {
int status;
String message;
List<UserFile> userFiles = new ArrayList<>();
if (!Algorithms.isEmpty(resultJson)) {
if (!Algorithms.isEmpty(error)) {
status = STATUS_SERVER_ERROR;
message = "Download file list error: " + parseServerError(error);
} else if (!Algorithms.isEmpty(resultJson)) {
try {
JSONObject result = new JSONObject(resultJson);
String totalZipSize = result.getString("totalZipSize");
@ -392,7 +425,7 @@ public class BackupHelper {
listener.onDownloadFileList(status, message, userFiles);
}
}
});
}, EXECUTOR);
}
public void downloadFiles(@NonNull final Map<File, UserFile> filesMap, @Nullable final OnDownloadFileListener listener) throws UserNotRegisteredException {
@ -420,6 +453,13 @@ public class BackupHelper {
}
}
@Override
public void onFileDownloadDone(@NonNull File file) {
if (listener != null) {
listener.onFileDownloaded(file);
}
}
@Override
public void onFileDownloadedAsync(@NonNull File file) {
if (listener != null) {
@ -430,10 +470,10 @@ public class BackupHelper {
@Override
public void onFilesDownloadDone(@NonNull Map<File, String> errors) {
if (listener != null) {
listener.onFilesDownloadDone(errors);
listener.onFilesDownloadDone(resolveServerErrors(errors));
}
}
});
}, EXECUTOR);
}
@SuppressLint("StaticFieldLeak")
@ -515,7 +555,37 @@ public class BackupHelper {
}
}
};
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
task.executeOnExecutor(EXECUTOR);
}
private Map<File, String> resolveServerErrors(@NonNull Map<File, String> errors) {
Map<File, String> resolvedErrors = new HashMap<>();
for (Entry<File, String> fileError : errors.entrySet()) {
File file = fileError.getKey();
String errorStr = fileError.getValue();
try {
JSONObject errorJson = new JSONObject(errorStr);
JSONObject error = errorJson.getJSONObject("error");
errorStr = "Error " + error.getInt("errorCode") + " (" + error.getString("message") + ")";
} catch (JSONException e) {
// ignore
}
resolvedErrors.put(file, errorStr);
}
return resolvedErrors;
}
private String parseServerError(@NonNull String error) {
try {
JSONObject resultError = new JSONObject(error);
if (resultError.has("error")) {
JSONObject errorObj = resultError.getJSONObject("error");
return errorObj.getInt("errorCode") + " (" + errorObj.getString("message") + ")";
}
} catch (JSONException e) {
// ignore
}
return error;
}
@SuppressLint("StaticFieldLeak")
@ -576,6 +646,6 @@ public class BackupHelper {
}
}
};
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
task.executeOnExecutor(EXECUTOR);
}
}

View file

@ -11,7 +11,6 @@ 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;
@ -113,7 +112,7 @@ public class BackupTask {
tasks.push(backupTasks[i]);
}
this.runningTasks = tasks;
onTasksInit();
onBackupTasksInit();
}
private void initRestoreTasks() {
@ -123,7 +122,7 @@ public class BackupTask {
tasks.push(restoreTasks[i]);
}
this.runningTasks = tasks;
onTasksInit();
onRestoreTasksInit();
}
private void initData() {
@ -180,6 +179,11 @@ public class BackupTask {
}
}
@Override
public void onFileUploadDone(@NonNull File file) {
onTaskProgressDone();
}
@Override
public void onFilesUploadDone(@NonNull Map<File, String> errors) {
uploadErrors = errors;
@ -224,6 +228,11 @@ public class BackupTask {
}
}
@Override
public void onFileDownloaded(@NonNull File file) {
onTaskProgressDone();
}
@Override
public void onFileDownloadedAsync(@NonNull File file) {
UserFile userFile = filesMap.get(file);
@ -275,14 +284,22 @@ public class BackupTask {
}
}
private void onTasksInit() {
private void onBackupTasksInit() {
Context ctx = contextRef.get();
if (ctx instanceof Activity && AndroidUtils.isActivityNotDestroyed((Activity) ctx) && progress != null) {
if (ctx instanceof Activity && AndroidUtils.isActivityNotDestroyed((Activity) ctx)) {
progress = ProgressImplementation.createProgressDialog(ctx,
"Backup data", "Initializing...", ProgressDialog.STYLE_HORIZONTAL);
}
}
private void onRestoreTasksInit() {
Context ctx = contextRef.get();
if (ctx instanceof Activity && AndroidUtils.isActivityNotDestroyed((Activity) ctx)) {
progress = ProgressImplementation.createProgressDialog(ctx,
"Restore data", "Initializing...", ProgressDialog.STYLE_HORIZONTAL);
}
}
private void onTaskProgressUpdate(Object... objects) {
Context ctx = contextRef.get();
if (ctx instanceof Activity && AndroidUtils.isActivityNotDestroyed((Activity) ctx) && progress != null) {
@ -292,7 +309,7 @@ public class BackupTask {
progress.startTask((String) objects[0], -1);
} else if (objects[0] instanceof Integer) {
int progressValue = (Integer) objects[0];
if (progressValue < Integer.MAX_VALUE) {
if (progressValue >= 0) {
progress.progress(progressValue);
} else {
progress.finishTask();
@ -305,6 +322,13 @@ public class BackupTask {
}
}
private void onTaskProgressDone() {
Context ctx = contextRef.get();
if (ctx instanceof Activity && AndroidUtils.isActivityNotDestroyed((Activity) ctx) && progress != null) {
progress.finishTask();
}
}
private void onError(@NonNull String message) {
this.error = message;
runningTasks.clear();

View file

@ -89,11 +89,6 @@ public class TestBackupActivity extends OsmandActionBarActivity {
}
});
if (!backupHelper.hasOsmLiveUpdates()) {
findViewById(R.id.main_view).setVisibility(View.GONE);
return;
}
buttonRegister = findViewById(R.id.btn_register);
UiUtilities.setupDialogButton(nightMode, buttonRegister, DialogButtonType.PRIMARY, "Register");
buttonVerify = findViewById(R.id.btn_verify);
@ -142,6 +137,7 @@ public class TestBackupActivity extends OsmandActionBarActivity {
a.buttonVerify.setVisibility(View.VISIBLE);
a.buttonVerify.setEnabled(status == BackupHelper.STATUS_SUCCESS);
a.tokenEditText.requestFocus();
a.infoView.setText(message);
}
}
});
@ -167,11 +163,12 @@ public class TestBackupActivity extends OsmandActionBarActivity {
a.progressBar.setVisibility(View.GONE);
a.buttonVerify.setEnabled(status != BackupHelper.STATUS_SUCCESS);
if (status == BackupHelper.STATUS_SUCCESS) {
tokenEdit.setVisibility(View.GONE);
buttonVerify.setVisibility(View.GONE);
}
a.tokenEdit.setVisibility(View.GONE);
a.buttonVerify.setVisibility(View.GONE);
a.prepareBackup();
}
a.infoView.setText(message);
}
}
});
} else {
@ -191,6 +188,7 @@ public class TestBackupActivity extends OsmandActionBarActivity {
@Override
public void onClick(View v) {
if (backupInfo != null) {
buttonBackup.setEnabled(false);
BackupTask task = new BackupTask(backupInfo, TestBackupActivity.this, new OnBackupListener() {
@Override
public void onBackupDone(@Nullable Map<File, String> uploadErrors, @Nullable Map<File, String> downloadErrors,
@ -200,14 +198,19 @@ public class TestBackupActivity extends OsmandActionBarActivity {
String description;
if (error != null) {
description = error;
} else if (uploadErrors == null && downloadErrors == null) {
} else if (uploadErrors == null && deleteErrors == null) {
description = "No data";
} else {
description = getBackupErrorsDescription(uploadErrors, downloadErrors, deleteErrors, error);
}
a.infoView.setText(description);
a.infoView.requestFocus();
a.buttonBackup.setEnabled(true);
if (Algorithms.isEmpty(description)) {
a.prepareBackup();
} else {
a.backupInfo = null;
}
}
}
});
@ -219,6 +222,7 @@ public class TestBackupActivity extends OsmandActionBarActivity {
@Override
public void onClick(View v) {
if (backupInfo != null) {
buttonRestore.setEnabled(false);
BackupTask task = new BackupTask(backupInfo, TestBackupActivity.this, new OnBackupListener() {
@Override
public void onBackupDone(@Nullable Map<File, String> uploadErrors, @Nullable Map<File, String> downloadErrors,
@ -235,7 +239,12 @@ public class TestBackupActivity extends OsmandActionBarActivity {
}
a.infoView.setText(description);
a.infoView.requestFocus();
a.buttonRestore.setEnabled(true);
if (Algorithms.isEmpty(description)) {
a.prepareBackup();
} else {
a.backupInfo = null;
}
}
}
});
@ -250,21 +259,21 @@ public class TestBackupActivity extends OsmandActionBarActivity {
private String getBackupErrorsDescription(@Nullable Map<File, String> uploadErrors, @Nullable Map<File, String> downloadErrors, @Nullable Map<UserFile, String> deleteErrors, @Nullable String error) {
StringBuilder sb = new StringBuilder();
if (!Algorithms.isEmpty(uploadErrors)) {
sb.append("--- Upload errors ---").append("\n");
sb.append("--- Upload errors ---").append("\n\n");
for (Entry<File, String> uploadEntry : uploadErrors.entrySet()) {
sb.append(uploadEntry.getKey().getName()).append(": ").append(uploadEntry.getValue()).append("\n");
sb.append(uploadEntry.getKey().getName()).append(": ").append(uploadEntry.getValue()).append("\n\n");
}
}
if (!Algorithms.isEmpty(downloadErrors)) {
sb.append("--- Download errors ---").append("\n");
sb.append("--- Download errors ---").append("\n\n");
for (Entry<File, String> downloadEntry : downloadErrors.entrySet()) {
sb.append(downloadEntry.getKey().getName()).append(": ").append(downloadEntry.getValue()).append("\n");
sb.append(downloadEntry.getKey().getName()).append(": ").append(downloadEntry.getValue()).append("\n\n");
}
}
if (!Algorithms.isEmpty(deleteErrors)) {
sb.append("--- Delete errors ---").append("\n");
sb.append("--- Delete errors ---").append("\n\n");
for (Entry<UserFile, String> deleteEntry : deleteErrors.entrySet()) {
sb.append(deleteEntry.getKey().getName()).append(": ").append(deleteEntry.getValue()).append("\n");
sb.append(deleteEntry.getKey().getName()).append(": ").append(deleteEntry.getValue()).append("\n\n");
}
}
return sb.length() == 0 ? "OK" : sb.toString();
@ -273,32 +282,32 @@ public class TestBackupActivity extends OsmandActionBarActivity {
private String getBackupDescription(@NonNull BackupInfo backupInfo) {
StringBuilder sb = new StringBuilder();
if (!Algorithms.isEmpty(backupInfo.filesToUpload)) {
sb.append("\n").append("--- Upload ---").append("\n");
sb.append("\n").append("--- Upload ---").append("\n\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");
.append("\n\n");
}
}
if (!Algorithms.isEmpty(backupInfo.filesToDownload)) {
sb.append("\n").append("--- Download ---").append("\n");
sb.append("\n").append("--- Download ---").append("\n\n");
for (UserFile userFile : backupInfo.filesToDownload) {
sb.append(userFile.getName())
.append(" R: ").append(DF.format(new Date(userFile.getClienttimems())))
.append("\n");
.append("\n\n");
}
}
if (!Algorithms.isEmpty(backupInfo.filesToDelete)) {
sb.append("\n").append("--- Delete ---").append("\n");
sb.append("\n").append("--- Delete ---").append("\n\n");
for (UserFile userFile : backupInfo.filesToDelete) {
sb.append(userFile.getName())
.append(" R: ").append(DF.format(new Date(userFile.getClienttimems())))
.append("\n");
.append("\n\n");
}
}
if (!Algorithms.isEmpty(backupInfo.filesToMerge)) {
sb.append("\n").append("--- Conflicts ---").append("\n");
sb.append("\n").append("--- Conflicts ---").append("\n\n");
for (Pair<GpxFileInfo, UserFile> localRemote : backupInfo.filesToMerge) {
GpxFileInfo local = localRemote.first;
UserFile remote = localRemote.second;
@ -306,7 +315,7 @@ public class TestBackupActivity extends OsmandActionBarActivity {
.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");
.append("\n\n");
}
}
return sb.toString();
@ -314,6 +323,7 @@ public class TestBackupActivity extends OsmandActionBarActivity {
private void prepareBackup() {
final WeakReference<TestBackupActivity> activityRef = new WeakReference<>(this);
buttonRefresh.setEnabled(false);
PrepareBackupTask prepareBackupTask = new PrepareBackupTask(this, new OnPrepareBackupListener() {
@Override
public void onBackupPrepared(@Nullable BackupInfo backupInfo, @Nullable String error) {
@ -334,6 +344,7 @@ public class TestBackupActivity extends OsmandActionBarActivity {
}
a.infoView.setText(description);
a.infoView.requestFocus();
a.buttonRefresh.setEnabled(true);
}
}
});

View file

@ -2125,9 +2125,12 @@ public class GpxUiHelper {
public static GPXFile makeGpxFromRoute(RouteCalculationResult route, OsmandApplication app) {
return makeGpxFromLocations(route.getRouteLocations(), app);
}
public static GPXFile makeGpxFromLocations(List<Location> locations, OsmandApplication app) {
double lastHeight = HEIGHT_UNDEFINED;
GPXFile gpx = new GPXUtilities.GPXFile(Version.getFullVersion(app));
List<Location> locations = route.getRouteLocations();
if (locations != null) {
GPXUtilities.Track track = new GPXUtilities.Track();
GPXUtilities.TrkSegment seg = new GPXUtilities.TrkSegment();
@ -2147,6 +2150,8 @@ public class GpxUiHelper {
}
}
lastHeight = h;
} else {
lastHeight = HEIGHT_UNDEFINED;
}
seg.points.add(point);
}

View file

@ -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);
}

View file

@ -8,6 +8,9 @@ import android.os.AsyncTask;
import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import net.osmand.AndroidNetworkUtils;
import net.osmand.AndroidNetworkUtils.OnRequestResultListener;
import net.osmand.AndroidNetworkUtils.OnSendRequestsListener;
@ -41,9 +44,6 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
public abstract class InAppPurchaseHelper {
// Debug tag, for logging
protected static final org.apache.commons.logging.Log LOG = PlatformUtil.getLog(InAppPurchaseHelper.class);
@ -466,7 +466,7 @@ public abstract class InAppPurchaseHelper {
protected void onSkuDetailsResponseDone(List<PurchaseInfo> purchaseInfoList) {
final AndroidNetworkUtils.OnRequestResultListener listener = new AndroidNetworkUtils.OnRequestResultListener() {
@Override
public void onResult(String result) {
public void onResult(@Nullable String result, @Nullable String error) {
notifyDismissProgress(InAppPurchaseTaskType.REQUEST_INVENTORY);
notifyGetItems();
stop(true);
@ -477,7 +477,7 @@ public abstract class InAppPurchaseHelper {
if (purchaseInfoList.size() > 0) {
sendTokens(purchaseInfoList, listener);
} else {
listener.onResult("OK");
listener.onResult("OK", null);
}
}
@ -503,7 +503,7 @@ public abstract class InAppPurchaseHelper {
liveUpdatesPurchase.setState(ctx, SubscriptionState.UNDEFINED);
sendTokens(Collections.singletonList(info), new OnRequestResultListener() {
@Override
public void onResult(String result) {
public void onResult(@Nullable String result, @Nullable String error) {
boolean active = ctx.getSettings().LIVE_UPDATES_PURCHASED.get();
ctx.getSettings().LIVE_UPDATES_PURCHASED.set(true);
ctx.getSettings().getCustomRenderBooleanProperty("depthContours").set(true);
@ -642,7 +642,7 @@ public abstract class InAppPurchaseHelper {
}
}
if (listener != null) {
listener.onResult("OK");
listener.onResult("OK", null);
}
}
@ -695,7 +695,7 @@ public abstract class InAppPurchaseHelper {
} catch (Exception e) {
logError("SendToken Error", e);
if (listener != null) {
listener.onResult("Error");
listener.onResult("Error", null);
}
}
}

View file

@ -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<ItineraryGroup> itineraryGroups = new ArrayList<>();
private Set<OnGroupSyncedListener> syncListeners = new HashSet<>();
public ItineraryHelper(@NonNull OsmandApplication app) {
this.app = app;
markersHelper = app.getMapMarkersHelper();
markersDbHelper = app.getMapMarkersDbHelper();
loadGroups();
}
public List<ItineraryGroup> 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<String> 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<MapMarker> getMapMarkersFromDefaultGroups(boolean history) {
List<MapMarker> 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<String, ItineraryGroup> groupsMap = markersDbHelper.getAllGroupsMap();
List<MapMarker> allMarkers = new ArrayList<>(markersHelper.getMapMarkers());
allMarkers.addAll(markersHelper.getMapMarkersHistory());
Iterator<Entry<String, ItineraryGroup>> 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<ItineraryGroup> copyList = new ArrayList<>(itineraryGroups);
copyList.add(group);
itineraryGroups = copyList;
}
public void removeFromGroupsList(ItineraryGroup group) {
List<ItineraryGroup> 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<ItineraryGroup>() {
@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<ItineraryGroup> getGroupsForDisplayedGpx() {
List<ItineraryGroup> res = new ArrayList<>();
List<SelectedGpxFile> 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<ItineraryGroup> getGroupsForSavedArticlesTravelBook() {
List<ItineraryGroup> res = new ArrayList<>();
TravelHelper travelHelper = app.getTravelHelper();
if (travelHelper.isAnyTravelBookPresent()) {
List<TravelArticle> 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<Void, Void, Void> {
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<MapMarker> 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<FavouritePoint> 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<WptPt> 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();
}
}
});
}
}
}
}

View file

@ -275,16 +275,14 @@ 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) {
dismiss();
}
});
if (closeButton instanceof ImageView) {
UiUtilities.rotateImageByLayoutDirection((ImageView) closeButton);
}
FrameLayout iconHelpContainer = toolbar.findViewById(R.id.action_button);
int iconColorResId = nightMode ? R.color.active_buttons_and_links_text_dark : R.color.active_buttons_and_links_text_light;
@ -443,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());

View file

@ -6,6 +6,7 @@ import android.content.Context;
import android.os.AsyncTask;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import net.osmand.AndroidNetworkUtils;
import net.osmand.AndroidNetworkUtils.OnRequestResultListener;
@ -194,7 +195,7 @@ public class PerformLiveUpdateAsyncTask
AndroidNetworkUtils.sendRequestAsync(
app, LiveUpdatesFragment.URL, null, "Requesting map updates info...", false, false, new OnRequestResultListener() {
@Override
public void onResult(String result) {
public void onResult(@Nullable String result, @Nullable String error) {
if (!Algorithms.isEmpty(result)) {
SimpleDateFormat source = new SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.US);
source.setTimeZone(TimeZone.getTimeZone("UTC"));

View file

@ -208,7 +208,7 @@ public class SubscriptionFragment extends BaseOsmAndDialogFragment implements In
"https://osmand.net/subscription/update",
parameters, "Sending data...", true, true, new AndroidNetworkUtils.OnRequestResultListener() {
@Override
public void onResult(String result) {
public void onResult(@Nullable String result, @Nullable String error) {
dismissProgress(null);
OsmandApplication app = getMyApplication();
if (result != null) {

View file

@ -189,7 +189,24 @@ public class TransportStopController extends MenuController {
}
}
private static void sortTransportStops(@NonNull LatLon latLon, List<TransportStop> transportStops) {
private static void sortTransportStopsExits(@NonNull LatLon latLon, @NonNull List<TransportStop> 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<TransportStop>() {
@Override
public int compare(TransportStop s1, TransportStop s2) {
return Algorithms.compare(s1.distance, s2.distance);
}
});
}
private static void sortTransportStops(@NonNull LatLon latLon, @NonNull List<TransportStop> 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);

View file

@ -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);
}
}
}

View file

@ -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);
}
}
}

View file

@ -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();
}
}

View file

@ -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();

View file

@ -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;
}
}

View file

@ -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();
}

View file

@ -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;
}
}

View file

@ -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) {

View file

@ -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<String, ItineraryGroup> getAllGroupsMap() {
Map<String, ItineraryGroup> res = new LinkedHashMap<>();
public Map<String, MapMarkersGroup> getAllGroupsMap() {
Map<String, MapMarkersGroup> 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));

View file

@ -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

View file

@ -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;
}

View file

@ -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<MapMarker> mapMarkers = new ArrayList<>();
private List<MapMarker> mapMarkersHistory = new ArrayList<>();
private List<MapMarkersGroup> mapMarkersGroups = new ArrayList<>();
private List<MapMarkerChangedListener> listeners = new ArrayList<>();
private Set<OnGroupSyncedListener> syncListeners = new HashSet<>();
private MarkersPlanRouteContext planRouteContext;
@ -81,24 +97,29 @@ public class MapMarkersHelper {
return mapMarkersHistory;
}
public List<MapMarkersGroup> 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<String, MapMarkersGroup> groupsMap = markersDbHelper.getAllGroupsMap();
List<MapMarker> allMarkers = new ArrayList<>(mapMarkers);
allMarkers.addAll(mapMarkersHistory);
Iterator<Map.Entry<String, MapMarkersGroup>> 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<MapMarker> markers, final boolean visited, final @MapMarkersSortByDef int sortByMode) {
private void sortMarkers(List<MapMarker> 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<MapMarker> 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<String> 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<MapMarkersGroup>() {
@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<MapMarkersGroup> getGroupsForDisplayedGpx() {
List<MapMarkersGroup> res = new ArrayList<>();
List<SelectedGpxFile> 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<MapMarkersGroup> getGroupsForSavedArticlesTravelBook() {
List<MapMarkersGroup> res = new ArrayList<>();
TravelHelper travelHelper = ctx.getTravelHelper();
if (travelHelper.isAnyTravelBookPresent()) {
List<TravelArticle> 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<MapMarker> getMarkers() {
List<MapMarker> 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,7 +631,7 @@ public class MapMarkersHelper {
return null;
}
public void addNewMarkerIfNeeded(@NonNull ItineraryGroup group,
private void addNewMarkerIfNeeded(@NonNull MapMarkersGroup group,
@NonNull List<MapMarker> groupMarkers,
@NonNull LatLon latLon,
@NonNull String name,
@ -406,7 +666,7 @@ public class MapMarkersHelper {
}
}
public void removeOldMarkersIfPresent(List<MapMarker> markers) {
private void removeOldMarkersIfPresent(List<MapMarker> 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<LatLon> points,
@NonNull List<PointDescription> historyNames,
@Nullable ItineraryGroup group) {
@Nullable MapMarkersGroup group) {
addMarkers(points, historyNames, group, null, null, null);
}
private void addMarkers(@NonNull List<LatLon> points,
@NonNull List<PointDescription> historyNames,
@Nullable ItineraryGroup group,
@Nullable MapMarkersGroup group,
@Nullable List<FavouritePoint> favouritePoints,
@Nullable List<WptPt> wptPts,
@Nullable List<String> mapObjNames) {
if (points.size() > 0) {
app.getSettings().SHOW_MAP_MARKERS.set(true);
ctx.getSettings().SHOW_MAP_MARKERS.set(true);
int colorIndex = -1;
List<MapMarker> 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<MapMarker> getMapMarkersFromDefaultGroups(boolean history) {
List<MapMarker> 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<MapMarker> 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<MapMarkersGroup> copyList = new ArrayList<>(mapMarkersGroups);
copyList.add(group);
mapMarkersGroups = copyList;
}
private void removeFromGroupsList(MapMarkersGroup group) {
List<MapMarkersGroup> 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<Void, Void, Void> {
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<MapMarker> 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<FavouritePoint> 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<WptPt> 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();
}
}
});
}
}
}
}

View file

@ -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() {

View file

@ -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<MapMarkerItemV
final int pos = holder.getAdapterPosition();
final MapMarker marker = getItem(pos);
mapActivity.getMyApplication().getMapMarkersHelper().moveMapMarkerToHistory(marker);
ItineraryGroup group = mapActivity.getMyApplication().getItineraryHelper().getMapMarkerGroupById(marker.groupKey,
ItineraryGroup.ANY_TYPE);
MapMarkersGroup group = mapActivity.getMyApplication().getMapMarkersHelper().getMapMarkerGroupById(marker.groupKey,
MapMarkersGroup.ANY_TYPE);
if (group != null) {
mapActivity.getMyApplication().getMapMarkersHelper().updateGroup(group);
}

View file

@ -7,8 +7,10 @@ import android.view.ViewGroup;
import android.widget.CompoundButton;
import android.widget.ImageView;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.appcompat.content.res.AppCompatResources;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.RecyclerView;
@ -21,12 +23,10 @@ import net.osmand.IndexConstants;
import net.osmand.data.LatLon;
import net.osmand.plus.GpxSelectionHelper;
import net.osmand.plus.GpxSelectionHelper.SelectedGpxFile;
import net.osmand.plus.itinerary.ItineraryHelper;
import net.osmand.plus.mapmarkers.CategoriesSubHeader;
import net.osmand.plus.mapmarkers.MapMarkersHelper;
import net.osmand.plus.mapmarkers.GroupHeader;
import net.osmand.plus.mapmarkers.MapMarker;
import net.osmand.plus.itinerary.ItineraryGroup;
import net.osmand.plus.mapmarkers.MapMarkersGroup;
import net.osmand.plus.mapmarkers.ShowHideHistoryButton;
import net.osmand.plus.OsmAndFormatter;
import net.osmand.plus.OsmandApplication;
@ -38,6 +38,7 @@ import net.osmand.plus.mapmarkers.SelectWptCategoriesBottomSheetDialogFragment;
import net.osmand.plus.wikivoyage.article.WikivoyageArticleDialogFragment;
import net.osmand.plus.wikivoyage.data.TravelArticle;
import net.osmand.plus.wikivoyage.data.TravelHelper;
import net.osmand.util.Algorithms;
import java.io.File;
import java.text.SimpleDateFormat;
@ -56,10 +57,6 @@ public class MapMarkersGroupsAdapter extends RecyclerView.Adapter<RecyclerView.V
private static final int SHOW_HIDE_HISTORY_TYPE = 3;
private static final int CATEGORIES_TYPE = 4;
private static final int TODAY_HEADER = 56;
private static final int YESTERDAY_HEADER = 57;
private static final int LAST_SEVEN_DAYS_HEADER = 58;
private static final int THIS_YEAR_HEADER = 59;
private MapActivity mapActivity;
private OsmandApplication app;
@ -104,55 +101,90 @@ public class MapMarkersGroupsAdapter extends RecyclerView.Adapter<RecyclerView.V
private void createDisplayGroups() {
items = new ArrayList<>();
ItineraryHelper helper = app.getItineraryHelper();
MapMarkersHelper helper = app.getMapMarkersHelper();
helper.updateGroups();
List<ItineraryGroup> groups = new ArrayList<>(helper.getItineraryGroups());
List<MapMarkersGroup> 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<MapMarker> groupMarkers = group.getActiveMarkers();
for (int j = 0; j < groupMarkers.size(); j++) {
MapMarker marker = groupMarkers.get(j);
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);
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;
currentGroupDateId = markerCalendar.get(Calendar.YEAR);
}
} else if (previousDateHeader != markerYear) {
items.add(markerYear);
previousDateHeader = markerYear;
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 + ""));
}
previousGroupDateId = currentGroupDateId;
}
items.add(marker);
}
@ -161,7 +193,7 @@ public class MapMarkersGroupsAdapter extends RecyclerView.Adapter<RecyclerView.V
items.add(header);
if (!group.isDisabled()) {
if (group.getWptCategories() != null && !group.getWptCategories().isEmpty()) {
CategoriesSubHeader categoriesSubHeader = group.getCategoriesSubHeader();
CategoriesSubHeader categoriesSubHeader = new CategoriesSubHeader(group);
items.add(categoriesSubHeader);
}
TravelHelper travelHelper = mapActivity.getMyApplication().getTravelHelper();
@ -177,7 +209,7 @@ public class MapMarkersGroupsAdapter extends RecyclerView.Adapter<RecyclerView.V
}
}
if (group.getWptCategories() == null || group.getWptCategories().isEmpty()) {
app.getItineraryHelper().updateGroupWptCategories(group, getGpxFile(group.getGpxPath()).getPointsByCategories().keySet());
helper.updateGroupWptCategories(group, getGpxFile(group.getGpxPath()).getPointsByCategories().keySet());
}
populateAdapterWithGroupMarkers(group, getItemCount());
}
@ -196,7 +228,7 @@ public class MapMarkersGroupsAdapter extends RecyclerView.Adapter<RecyclerView.V
return null;
}
private void populateAdapterWithGroupMarkers(ItineraryGroup group, int position) {
private void populateAdapterWithGroupMarkers(MapMarkersGroup group, int position) {
if (position != RecyclerView.NO_POSITION) {
ShowHideHistoryButton showHideHistoryButton = group.getShowHideHistoryButton();
if (!group.isDisabled()) {
@ -221,7 +253,7 @@ public class MapMarkersGroupsAdapter extends RecyclerView.Adapter<RecyclerView.V
public int getGroupHeaderPosition(String groupId) {
int pos = -1;
ItineraryGroup group = app.getItineraryHelper().getMapMarkerGroupById(groupId, ItineraryGroup.ANY_TYPE);
MapMarkersGroup group = app.getMapMarkersHelper().getMapMarkerGroupById(groupId, MapMarkersGroup.ANY_TYPE);
if (group != null) {
pos = items.indexOf(group.getGroupHeader());
}
@ -381,32 +413,19 @@ public class MapMarkersGroupsAdapter extends RecyclerView.Adapter<RecyclerView.V
final MapMarkerHeaderViewHolder headerViewHolder = (MapMarkerHeaderViewHolder) holder;
final Object header = getItem(position);
String headerString;
if (header instanceof Integer) {
if (header instanceof MarkerGroupItem) {
headerViewHolder.icon.setVisibility(View.GONE);
headerViewHolder.iconSpace.setVisibility(View.VISIBLE);
Integer dateHeader = (Integer) header;
if (dateHeader == TODAY_HEADER) {
headerString = app.getString(R.string.today);
} else if (dateHeader == YESTERDAY_HEADER) {
headerString = app.getString(R.string.yesterday);
} else if (dateHeader == LAST_SEVEN_DAYS_HEADER) {
headerString = app.getString(R.string.last_seven_days);
} else if (dateHeader == THIS_YEAR_HEADER) {
headerString = app.getString(R.string.this_year);
} else if (dateHeader / 100 == 0) {
headerString = getMonth(dateHeader);
} else {
headerString = String.valueOf(dateHeader);
}
headerString = ((MarkerGroupItem) header).getName(app);
headerViewHolder.disableGroupSwitch.setVisibility(View.GONE);
headerViewHolder.articleDescription.setVisibility(View.GONE);
} else if (header instanceof GroupHeader) {
final GroupHeader groupHeader = (GroupHeader) header;
final ItineraryGroup group = groupHeader.getGroup();
final MapMarkersGroup group = groupHeader.getGroup();
String groupName = group.getName();
if (groupName.isEmpty()) {
groupName = app.getString(R.string.shared_string_favorites);
} else if (group.getType() == ItineraryGroup.GPX_TYPE) {
} else if (group.getType() == MapMarkersGroup.GPX_TYPE) {
groupName = groupName.replace(IndexConstants.GPX_FILE_EXT, "").replace("/", " ").replace("_", " ");
}
if (group.isDisabled()) {
@ -463,8 +482,8 @@ public class MapMarkersGroupsAdapter extends RecyclerView.Adapter<RecyclerView.V
fragment.setUsedOnMap(false);
fragment.show(mapActivity.getSupportFragmentManager(), SelectWptCategoriesBottomSheetDialogFragment.TAG);
}
app.getItineraryHelper().updateGroupDisabled(group, disabled);
if (group.getType() == ItineraryGroup.GPX_TYPE) {
mapMarkersHelper.updateGroupDisabled(group, disabled);
if (group.getType() == MapMarkersGroup.GPX_TYPE) {
group.setVisibleUntilRestart(disabled);
String gpxPath = group.getGpxPath();
SelectedGpxFile selectedGpxFile = app.getSelectedGpxHelper().getSelectedFileByPath(gpxPath);
@ -477,9 +496,9 @@ public class MapMarkersGroupsAdapter extends RecyclerView.Adapter<RecyclerView.V
switchGpxVisibility(gpxFile[0], selectedGpxFile, !disabled);
}
if(!disabled) {
app.getItineraryHelper().enableGroup(group);
mapMarkersHelper.enableGroup(group);
} else {
app.getItineraryHelper().runSynchronization(group);
mapMarkersHelper.runSynchronization(group);
}
if (disabled) {
@ -487,10 +506,10 @@ public class MapMarkersGroupsAdapter extends RecyclerView.Adapter<RecyclerView.V
.setAction(R.string.shared_string_undo, new View.OnClickListener() {
@Override
public void onClick(View view) {
if (group.getType() == ItineraryGroup.GPX_TYPE && gpxFile[0] != null) {
if (group.getType() == MapMarkersGroup.GPX_TYPE && gpxFile[0] != null) {
switchGpxVisibility(gpxFile[0], null, true);
}
app.getItineraryHelper().enableGroup(group);
mapMarkersHelper.enableGroup(group);
}
});
UiUtilities.setupSnackbar(snackbar, night);
@ -530,7 +549,7 @@ public class MapMarkersGroupsAdapter extends RecyclerView.Adapter<RecyclerView.V
final Object header = getItem(position);
if (header instanceof CategoriesSubHeader) {
final CategoriesSubHeader categoriesSubHeader = (CategoriesSubHeader) header;
final ItineraryGroup group = categoriesSubHeader.getGroup();
final MapMarkersGroup group = categoriesSubHeader.getGroup();
View.OnClickListener openChooseCategoriesDialog = new View.OnClickListener() {
@Override
public void onClick(View view) {
@ -544,7 +563,7 @@ public class MapMarkersGroupsAdapter extends RecyclerView.Adapter<RecyclerView.V
fragment.setUsedOnMap(false);
fragment.show(mapActivity.getSupportFragmentManager(), SelectWptCategoriesBottomSheetDialogFragment.TAG);
} else {
mapActivity.getMyApplication().getItineraryHelper().addOrEnableGpxGroup(new File(group.getGpxPath()));
mapActivity.getMyApplication().getMapMarkersHelper().addOrEnableGpxGroup(new File(group.getGpxPath()));
}
}
};
@ -573,7 +592,7 @@ public class MapMarkersGroupsAdapter extends RecyclerView.Adapter<RecyclerView.V
}
}
private String getGroupWptCategoriesString(ItineraryGroup group) {
private String getGroupWptCategoriesString(MapMarkersGroup group) {
StringBuilder sb = new StringBuilder();
Set<String> categories = group.getWptCategories();
if (categories != null && !categories.isEmpty()) {
@ -617,17 +636,46 @@ public class MapMarkersGroupsAdapter extends RecyclerView.Adapter<RecyclerView.V
return items.get(position);
}
private String getMonth(int month) {
SimpleDateFormat dateFormat = new SimpleDateFormat("LLLL", Locale.getDefault());
Date date = new Date();
date.setMonth(month);
String monthStr = dateFormat.format(date);
if (monthStr.length() > 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;
}
return monthStr;
public MarkerGroupItem(String name) {
this.name = name;
}
public String getName(OsmandApplication app) {
if (name == null && iname != 0) {
name = app.getString(iname);
}
return name;
}
}
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);

View file

@ -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<Void, Void, Void> {
}
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);
}
}

View file

@ -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<String> categories = markersGroup.getWptCategories();
Set<String> 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);
}
}
}

View file

@ -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));
}

View file

@ -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);
}

View file

@ -6,12 +6,14 @@ import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import net.osmand.plus.track.GradientScaleType;
import net.osmand.util.Algorithms;
public class RouteLineDrawInfo {
private static final String LINE_COLOR_DAY = "line_color_day";
private static final String LINE_COLOR_NIGHT = "line_color_night";
private static final String LINE_COLOR_GRADIENT = "line_color_gradient";
private static final String LINE_WIDTH = "line_width";
private static final String NAVIGATION_ICON_ID = "navigation_icon_id";
private static final String NAVIGATION_ICON_COLOR = "navigation_icon_color";
@ -24,6 +26,7 @@ public class RouteLineDrawInfo {
@ColorInt
private Integer colorDay;
private Integer colorNight;
private GradientScaleType scaleType;
private String width;
// temporally parameters to show in preview
@ -37,9 +40,11 @@ public class RouteLineDrawInfo {
public RouteLineDrawInfo(@Nullable @ColorInt Integer colorDay,
@Nullable @ColorInt Integer colorNight,
@Nullable GradientScaleType gradientScaleType,
@Nullable String width) {
this.colorDay = colorDay;
this.colorNight = colorNight;
this.scaleType = gradientScaleType;
this.width = width;
}
@ -50,6 +55,7 @@ public class RouteLineDrawInfo {
public RouteLineDrawInfo(@NonNull RouteLineDrawInfo existed) {
this.colorDay = existed.colorDay;
this.colorNight = existed.colorNight;
this.scaleType = existed.scaleType;
this.width = existed.width;
this.iconId = existed.iconId;
this.iconColor = existed.iconColor;
@ -71,6 +77,10 @@ public class RouteLineDrawInfo {
this.useDefaultColor = useDefaultColor;
}
public void setGradientScaleType(@Nullable GradientScaleType scaleType) {
this.scaleType = scaleType;
}
public void setWidth(@Nullable String width) {
this.width = width;
}
@ -108,6 +118,11 @@ public class RouteLineDrawInfo {
return nightMode ? colorNight : colorDay;
}
@Nullable
public GradientScaleType getGradientScaleType() {
return scaleType;
}
@Nullable
public String getWidth() {
return width;
@ -141,6 +156,9 @@ public class RouteLineDrawInfo {
if (bundle.containsKey(LINE_COLOR_NIGHT)) {
colorNight = bundle.getInt(LINE_COLOR_NIGHT);
}
if (bundle.containsKey(LINE_COLOR_GRADIENT)) {
scaleType = GradientScaleType.getGradientTypeByName(bundle.getString(LINE_COLOR_GRADIENT));
}
width = bundle.getString(LINE_WIDTH);
iconId = bundle.getInt(NAVIGATION_ICON_ID);
iconColor = bundle.getInt(NAVIGATION_ICON_COLOR);
@ -157,6 +175,9 @@ public class RouteLineDrawInfo {
if (colorNight != null) {
bundle.putInt(LINE_COLOR_NIGHT, colorNight);
}
if (scaleType != null) {
bundle.putString(LINE_COLOR_GRADIENT, scaleType.getTypeName());
}
if (width != null) {
bundle.putString(LINE_WIDTH, width);
}
@ -177,6 +198,7 @@ public class RouteLineDrawInfo {
if (!Algorithms.objectEquals(getColor(false), that.getColor(false))) return false;
if (!Algorithms.objectEquals(getColor(true), that.getColor(true))) return false;
if (!Algorithms.objectEquals(scaleType, that.scaleType)) return false;
return Algorithms.objectEquals(width, that.width);
}
@ -184,6 +206,7 @@ public class RouteLineDrawInfo {
public int hashCode() {
int result = colorDay != null ? colorDay.hashCode() : 0;
result = 31 * result + (colorNight != null ? colorNight.hashCode() : 0);
result = 31 * result + (scaleType != null ? scaleType.getTypeName().hashCode() : 0);
result = 31 * result + (width != null ? width.hashCode() : 0);
return result;
}

View file

@ -21,6 +21,7 @@ import net.osmand.AndroidUtils;
import net.osmand.plus.R;
import net.osmand.plus.UiUtilities;
import net.osmand.plus.activities.MapActivity;
import net.osmand.plus.helpers.AndroidUiHelper;
import net.osmand.plus.helpers.ColorDialogs;
import net.osmand.plus.helpers.enums.DayNightMode;
import net.osmand.plus.routepreparationmenu.cards.BaseCard;
@ -32,6 +33,8 @@ import net.osmand.plus.settings.fragments.HeaderUiAdapter;
import net.osmand.plus.track.AppearanceViewHolder;
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.multistatetoggle.RadioItem;
import net.osmand.plus.widgets.multistatetoggle.RadioItem.OnRadioItemClickListener;
import net.osmand.plus.widgets.multistatetoggle.TextToggleButton;
@ -51,6 +54,7 @@ public class RouteLineColorCard extends BaseCard implements CardListener, ColorP
private HeaderUiAdapter headerUiAdapter;
private ColorsCard colorsCard;
private GradientCard gradientCard;
private ColorTypeAdapter colorAdapter;
private RecyclerView groupRecyclerView;
private TextView tvDescription;
@ -64,7 +68,9 @@ public class RouteLineColorCard extends BaseCard implements CardListener, ColorP
private enum ColorMode {
DEFAULT(R.string.map_widget_renderer, R.drawable.ic_action_map_style),
CUSTOM(R.string.shared_string_custom, R.drawable.ic_action_settings);
CUSTOM(R.string.shared_string_custom, R.drawable.ic_action_settings),
ALTITUDE(R.string.altitude, R.drawable.ic_action_hillshade_dark),
SLOPE(R.string.shared_string_slope, R.drawable.ic_action_altitude_ascent);
ColorMode(int titleId, int iconId) {
this.titleId = titleId;
@ -108,28 +114,43 @@ public class RouteLineColorCard extends BaseCard implements CardListener, ColorP
setupRadioGroup(radioGroup);
cardsContainer = (ViewGroup) view.findViewById(R.id.colors_card_container);
createColorSelector(cardsContainer);
createCards(cardsContainer);
initSelectedMode();
}
private void initSelectedMode() {
if (routeLineDrawInfo.getGradientScaleType() == GradientScaleType.ALTITUDE) {
selectedMode = ColorMode.ALTITUDE;
} else if (routeLineDrawInfo.getGradientScaleType() == GradientScaleType.SLOPE) {
selectedMode = ColorMode.SLOPE;
} else {
selectedMode = getRouteLineColor() == null ? ColorMode.DEFAULT : ColorMode.CUSTOM;
}
modeChanged();
}
private void modeChanged() {
if (selectedMode == ColorMode.DEFAULT) {
themeToggleContainer.setVisibility(View.GONE);
cardsContainer.setVisibility(View.GONE);
AndroidUiHelper.updateVisibility(colorsCard.getView(), false);
AndroidUiHelper.updateVisibility(gradientCard.getView(), false);
routeLineDrawInfo.setUseDefaultColor(true);
changeMapTheme(initMapTheme);
} else {
} else if (selectedMode == ColorMode.CUSTOM) {
themeToggleContainer.setVisibility(View.VISIBLE);
cardsContainer.setVisibility(View.VISIBLE);
AndroidUiHelper.updateVisibility(colorsCard.getView(), true);
AndroidUiHelper.updateVisibility(gradientCard.getView(), false);
routeLineDrawInfo.setUseDefaultColor(false);
changeMapTheme(isNightMap() ? DayNightMode.NIGHT : DayNightMode.DAY);
} else {
gradientCard.setSelectedScaleType(getGradientScaleTypeFromMode());
AndroidUiHelper.updateVisibility(colorsCard.getView(), false);
AndroidUiHelper.updateVisibility(gradientCard.getView(), true);
themeToggleContainer.setVisibility(View.GONE);
routeLineDrawInfo.setUseDefaultColor(false);
}
routeLineDrawInfo.setGradientScaleType(getGradientScaleTypeFromMode());
updateColorItems();
updateDescription();
}
@ -170,7 +191,7 @@ public class RouteLineColorCard extends BaseCard implements CardListener, ColorP
}
}
private void createColorSelector(ViewGroup container) {
private void createCards(ViewGroup container) {
MapActivity mapActivity = getMapActivity();
if (mapActivity != null) {
List<Integer> colors = new ArrayList<>();
@ -184,6 +205,9 @@ public class RouteLineColorCard extends BaseCard implements CardListener, ColorP
colorsCard = new ColorsCard(mapActivity, selectedColor, targetFragment, colors, preference, null);
colorsCard.setListener(this);
container.addView(colorsCard.build(mapActivity));
gradientCard = new GradientCard(mapActivity, routeLineDrawInfo.getGradientScaleType());
container.addView(gradientCard.build(mapActivity));
}
}
@ -212,6 +236,17 @@ public class RouteLineColorCard extends BaseCard implements CardListener, ColorP
return routeLineDrawInfo.getColor(isNightMap());
}
@Nullable
private GradientScaleType getGradientScaleTypeFromMode() {
if (selectedMode == ColorMode.ALTITUDE) {
return GradientScaleType.ALTITUDE;
} else if (selectedMode == ColorMode.SLOPE) {
return GradientScaleType.SLOPE;
} else {
return null;
}
}
private void updateSelectedColor() {
int selectedColor = colorsCard.getSelectedColor();
routeLineDrawInfo.setColor(selectedColor, isNightMap());
@ -235,6 +270,8 @@ public class RouteLineColorCard extends BaseCard implements CardListener, ColorP
String colorName = "";
if (selectedMode == ColorMode.DEFAULT) {
colorName = app.getString(R.string.map_widget_renderer);
} else if (selectedMode == ColorMode.ALTITUDE || selectedMode == ColorMode.SLOPE) {
colorName = app.getString(selectedMode.titleId);
} else if (getRouteLineColor() != null) {
int colorNameId = ColorDialogs.getColorName(getRouteLineColor());
colorName = app.getString(colorNameId);
@ -248,10 +285,12 @@ public class RouteLineColorCard extends BaseCard implements CardListener, ColorP
String pattern = app.getString(R.string.route_line_use_map_style_appearance);
String color = app.getString(R.string.shared_string_color).toLowerCase();
description = String.format(pattern, color, app.getRendererRegistry().getSelectedRendererName());
} else {
} else if (selectedMode == ColorMode.CUSTOM) {
String pattern = app.getString(R.string.specify_color_for_map_mode);
String mapModeTitle = app.getString(isNightMap() ? NIGHT_TITLE_ID : DAY_TITLE_ID);
description = String.format(pattern, mapModeTitle.toLowerCase());
} else {
description = app.getString(R.string.route_line_use_gradient_coloring);
}
tvDescription.setText(description);
}

View file

@ -6,6 +6,8 @@ import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.Nullable;
import net.osmand.AndroidNetworkUtils;
import net.osmand.plus.OsmandApplication;
import net.osmand.plus.R;
@ -71,7 +73,7 @@ public class SendSearchQueryBottomSheet extends MenuBottomSheetDialogFragment {
AndroidNetworkUtils.sendRequestAsync(app, "https://osmand.net/api/missing_search", params,
null, true, true, new AndroidNetworkUtils.OnRequestResultListener() {
@Override
public void onResult(String result) {
public void onResult(@Nullable String result, @Nullable String error) {
if (result != null && isAdded()) {
try {
JSONObject obj = new JSONObject(result);

View file

@ -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);
}
@ -2708,6 +2708,7 @@ public class OsmandSettings {
public final ListStringPreference CUSTOM_ROUTE_LINE_COLORS = (ListStringPreference) new ListStringPreference(this, "custom_route_line_colors", null, ",").makeShared().makeGlobal();
public final CommonPreference<Integer> ROUTE_LINE_COLOR_DAY = new IntPreference(this, "route_line_color", 0).cache().makeProfile();
public final CommonPreference<Integer> ROUTE_LINE_COLOR_NIGHT = new IntPreference(this, "route_line_color_night", 0).cache().makeProfile();
public final CommonPreference<GradientScaleType> ROUTE_LINE_GRADIENT = new EnumStringPreference<>(this, "route_line_gradient", null, new GradientScaleType[] {GradientScaleType.ALTITUDE, GradientScaleType.SLOPE}).cache().makeProfile();
public final CommonPreference<String> ROUTE_LINE_WIDTH = new StringPreference(this, "route_line_width", null).makeProfile();
public final OsmandPreference<Boolean> USE_OSM_LIVE_FOR_ROUTING = new BooleanPreference(this, "enable_osmc_routing", true).makeProfile();

View file

@ -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<MapMarker
protected void init() {
super.init();
markersHelper = app.getMapMarkersHelper();
existingItems = new ArrayList<>(app.getItineraryHelper().getMapMarkersFromDefaultGroups(true));
existingItems = new ArrayList<>(markersHelper.getMapMarkersFromDefaultGroups(true));
}
@NonNull
@ -122,10 +122,10 @@ public class HistoryMarkersSettingsItem extends CollectionSettingsItem<MapMarker
}
}
public ItineraryGroup getMarkersGroup() {
public MapMarkersGroup getMarkersGroup() {
String name = app.getString(R.string.markers_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(items);
return markersGroup;
}

View file

@ -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 MarkersSettingsItem extends CollectionSettingsItem<MapMarker> {
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<MapMarker> {
}
}
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;
}

View file

@ -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<MapMarker> mapMarkers = app.getItineraryHelper().getMapMarkersFromDefaultGroups(false);
List<MapMarker> 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<MapMarker> markersHistory = app.getItineraryHelper().getMapMarkersFromDefaultGroups(true);
List<MapMarker> 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<FavoriteGroup> favoriteGroups = new ArrayList<>();
List<OsmNotesPoint> osmNotesPointList = new ArrayList<>();
List<OpenstreetmapPoint> osmEditsPointList = new ArrayList<>();
List<ItineraryGroup> markersGroups = new ArrayList<>();
List<ItineraryGroup> markersHistoryGroups = new ArrayList<>();
List<MapMarkersGroup> markersGroups = new ArrayList<>();
List<MapMarkersGroup> markersHistoryGroups = new ArrayList<>();
List<HistoryEntry> historyEntries = new ArrayList<>();
List<OnlineRoutingEngine> 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<MapMarker> 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<MapMarker> 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<OsmNotesPoint> notesPointList = new ArrayList<>();
List<OpenstreetmapPoint> editsPointList = new ArrayList<>();
List<FavoriteGroup> favoriteGroups = new ArrayList<>();
List<ItineraryGroup> markersGroups = new ArrayList<>();
List<ItineraryGroup> markersHistoryGroups = new ArrayList<>();
List<MapMarkersGroup> markersGroups = new ArrayList<>();
List<MapMarkersGroup> markersHistoryGroups = new ArrayList<>();
List<HistoryEntry> historyEntries = new ArrayList<>();
List<OnlineRoutingEngine> onlineRoutingEngines = new ArrayList<>();

View file

@ -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 {

View file

@ -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};

View file

@ -41,7 +41,7 @@ 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;
@ -377,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)));

Some files were not shown because too many files have changed in this diff Show more