Compare commits

...

306 commits

Author SHA1 Message Date
f4366688b7 [docs] Fix logo in readme
Some checks failed
Android Check / Android Lint (push) Has been cancelled
Android Check / Build Android Debug (push) Has been cancelled
iOS Check / Build iOS (push) Has been cancelled
Linux Check / Linux no unity build (push) Has been cancelled
Linux Check / Linux builds and tests (push) Has been cancelled
macOS Check / macOS builds and tests (push) Has been cancelled
Signed-off-by: RedAuburn <me@hbond.xyz>
2025-03-20 12:17:14 +00:00
90772d66d2 [android] Fix tiny transparent PP edges after it fills the screen
Resolves #9335

Signed-off-by: savsch <119003089+savsch@users.noreply.github.com>
2025-03-13 12:45:42 +03:00
cf00843b0a Hide specific preferences in settings for improved user experience
Signed-off-by: DevarshVasani <vasanidevarsh@gmail.com>
2025-03-13 12:29:22 +03:00
renderexpert
58546f3115 Fixed gamma correction
Signed-off-by: renderexpert <expert@renderconsulting.co.uk>
2025-03-13 07:14:16 +03:00
renderexpert
cddfc2e891 Add developer sandbox desktop app
Signed-off-by: renderexpert <expert@renderconsulting.co.uk>
2025-03-13 07:14:16 +03:00
renderexpert
2bdf6763ce Add glfw, imgui and cmake metal dependencies
Signed-off-by: renderexpert <expert@renderconsulting.co.uk>
2025-03-13 07:14:16 +03:00
renderexpert
a54f5268cd Add imGui shaders
Signed-off-by: renderexpert <expert@renderconsulting.co.uk>
2025-03-13 07:14:16 +03:00
Ferenc Géczi
466b9365f6 Add 2025.03.02 release entry to metainfo.xml
Signed-off-by: Ferenc Géczi <ferenc.gm@gmail.com>
2025-03-09 11:27:49 +03:00
6bf8338cdd [github] Add edits.xml to .gitignore
it's generated when making edits on desktop
Signed-off-by: Harry Bond <me@hbond.xyz>
2025-03-08 17:47:49 +00:00
Kuzey Bilgin
95a2cf3bc5 [github] Add stale bot to close very old PRs
Signed-off-by: Kuzey Bilgin <kuzeybilgin@proton.me>
2025-03-08 17:47:37 +00:00
cf7cce69fa [android][sdk] Search SDK
Signed-off-by: Andrei Shkrob <github@shkrob.dev>
2025-03-08 17:39:52 +00:00
d95bfeb0fd [strings] Update translations
Co-authored-by: Adolfo Jayme Barrientos <fitojb@ubuntu.com>
Co-authored-by: Am Heu <amanuel.amalio@thefluent.org>
Co-authored-by: Christos Sidiropoulos <dev@csidirop.de>
Co-authored-by: Dan <jonweblin2205@protonmail.com>
Co-authored-by: Dmitry Gribenchuk <dmitrygribenchuk@gmail.com>
Co-authored-by: ERYpTION <eryption.ar9q2@slmail.me>
Co-authored-by: Eryk Michalak <gnu.ewm@protonmail.com>
Co-authored-by: Fjuro <fjuro@users.noreply.hosted.weblate.org>
Co-authored-by: Gonzalo Pesquero <gpesquero@yahoo.es>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Lens0021 <lorentz0021@gmail.com>
Co-authored-by: Maschga <delphi@freenet.de>
Co-authored-by: Matheus Gomes Correia <matheus.gomes03@hotmail.com>
Co-authored-by: Priit Jõerüüt <hwlate@joeruut.com>
Co-authored-by: Rihards Olups <richlv@nakts.net>
Co-authored-by: Rimas Kudelis <rimas@kudelis.lt>
Co-authored-by: Roman Tsisyk <roman@organicmaps.app>
Co-authored-by: Software In Interlingua <softinterlingua@gmail.com>
Co-authored-by: Weblate Translation Memory <noreply-mt-weblate-translation-memory@weblate.org>
Co-authored-by: nautilusx <translate@disroot.org>
Co-authored-by: soshial <soshial@gmail.com>
Signed-off-by: Adolfo Jayme Barrientos <fitojb@ubuntu.com>
Signed-off-by: Am Heu <amanuel.amalio@thefluent.org>
Signed-off-by: Christos Sidiropoulos <dev@csidirop.de>
Signed-off-by: Dan <jonweblin2205@protonmail.com>
Signed-off-by: Dmitry Gribenchuk <dmitrygribenchuk@gmail.com>
Signed-off-by: ERYpTION <eryption.ar9q2@slmail.me>
Signed-off-by: Eryk Michalak <gnu.ewm@protonmail.com>
Signed-off-by: Fjuro <fjuro@users.noreply.hosted.weblate.org>
Signed-off-by: Gonzalo Pesquero <gpesquero@yahoo.es>
Signed-off-by: Lens0021 <lorentz0021@gmail.com>
Signed-off-by: Maschga <delphi@freenet.de>
Signed-off-by: Matheus Gomes Correia <matheus.gomes03@hotmail.com>
Signed-off-by: Priit Jõerüüt <hwlate@joeruut.com>
Signed-off-by: Rihards Olups <richlv@nakts.net>
Signed-off-by: Rimas Kudelis <rimas@kudelis.lt>
Signed-off-by: Roman Tsisyk <roman@organicmaps.app>
Signed-off-by: Roman Tsisyk <roman@tsisyk.com>
Signed-off-by: Software In Interlingua <softinterlingua@gmail.com>
Signed-off-by: Weblate <hosted@weblate.org>
Signed-off-by: Weblate Translation Memory <noreply-mt-weblate-translation-memory@weblate.org>
Signed-off-by: nautilusx <translate@disroot.org>
Signed-off-by: soshial <soshial@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android-typestrings/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android-typestrings/ca/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android-typestrings/da/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android-typestrings/de/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android-typestrings/es/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android-typestrings/et/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android-typestrings/ia/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android-typestrings/pl/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android-typestrings/pt_BR/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android-typestrings/uk/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android/ast/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android/be/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android/ca/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android/da/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android/de/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android/el/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android/es/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android/et/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android/ia/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android/ko/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android/lv/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android/ru/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android/uk/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/countries/ast/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/countries/ca/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/countries/es/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/countries/ia/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/countries/lt/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-plist/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-plist/es/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-plurals/af/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-plurals/ar/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-plurals/da/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-plurals/de/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-plurals/el/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-plurals/es/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-plurals/fr/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-plurals/hi/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-plurals/it/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-plurals/ja/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-plurals/nb_NO/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-plurals/pt/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-plurals/pt_BR/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-plurals/ro/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-plurals/ru/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-plurals/sv/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-plurals/tr/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-plurals/uk/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-plurals/zh_Hant/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-typestrings/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-typestrings/ca/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-typestrings/da/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-typestrings/de/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-typestrings/es/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-typestrings/et/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-typestrings/pl/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-typestrings/pt_BR/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-typestrings/uk/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios/ast/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios/ca/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios/da/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios/de/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios/el/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios/es/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios/et/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios/ko/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios/lv/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios/ru/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios/uk/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/tts/cs/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/tts/de/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/tts/et/
Translation: OrganicMaps/Country names for downloader
2025-03-08 09:06:45 +00:00
575e17d63c [strings] Update translations
Co-authored-by: Adolfo Jayme Barrientos <fitojb@ubuntu.com>
Co-authored-by: Dan <jonweblin2205@protonmail.com>
Co-authored-by: Dmitry Gribenchuk <dmitrygribenchuk@gmail.com>
Co-authored-by: ERYpTION <eryption.ar9q2@slmail.me>
Co-authored-by: Eryk Michalak <gnu.ewm@protonmail.com>
Co-authored-by: Fjuro <fjuro@users.noreply.hosted.weblate.org>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Lens0021 <lorentz0021@gmail.com>
Co-authored-by: Maschga <delphi@freenet.de>
Co-authored-by: Priit Jõerüüt <hwlate@joeruut.com>
Co-authored-by: Rihards Olups <richlv@nakts.net>
Co-authored-by: Rimas Kudelis <rimas@kudelis.lt>
Co-authored-by: Roman Tsisyk <roman@organicmaps.app>
Co-authored-by: Software In Interlingua <softinterlingua@gmail.com>
Co-authored-by: Weblate Translation Memory <noreply-mt-weblate-translation-memory@weblate.org>
Co-authored-by: nautilusx <translate@disroot.org>
Co-authored-by: soshial <soshial@gmail.com>
Signed-off-by: Adolfo Jayme Barrientos <fitojb@ubuntu.com>
Signed-off-by: Dan <jonweblin2205@protonmail.com>
Signed-off-by: Dmitry Gribenchuk <dmitrygribenchuk@gmail.com>
Signed-off-by: ERYpTION <eryption.ar9q2@slmail.me>
Signed-off-by: Eryk Michalak <gnu.ewm@protonmail.com>
Signed-off-by: Fjuro <fjuro@users.noreply.hosted.weblate.org>
Signed-off-by: Lens0021 <lorentz0021@gmail.com>
Signed-off-by: Maschga <delphi@freenet.de>
Signed-off-by: Priit Jõerüüt <hwlate@joeruut.com>
Signed-off-by: Rihards Olups <richlv@nakts.net>
Signed-off-by: Rimas Kudelis <rimas@kudelis.lt>
Signed-off-by: Roman Tsisyk <roman@organicmaps.app>
Signed-off-by: Software In Interlingua <softinterlingua@gmail.com>
Signed-off-by: Weblate Translation Memory <noreply-mt-weblate-translation-memory@weblate.org>
Signed-off-by: nautilusx <translate@disroot.org>
Signed-off-by: soshial <soshial@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android-typestrings/ca/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android-typestrings/da/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android-typestrings/de/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android-typestrings/es/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android-typestrings/et/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android-typestrings/ia/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android-typestrings/pl/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android/ast/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android/be/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android/ca/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android/da/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android/de/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android/es/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android/et/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android/ia/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android/ko/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android/lv/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android/ru/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android/uk/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/countries/ast/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/countries/ca/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/countries/es/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/countries/lt/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-plist/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-plist/es/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-plurals/da/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-plurals/de/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-plurals/es/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-typestrings/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-typestrings/ca/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-typestrings/da/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-typestrings/de/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-typestrings/es/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-typestrings/et/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-typestrings/pl/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios/ca/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios/da/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios/de/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios/es/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios/et/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios/ko/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios/lv/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios/ru/
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios/uk/
2025-03-06 19:14:28 +00:00
Gideon Wentink
bac9073bcb [strings] Generate Afrikaans (af) from strings.txt
The language was present in strings.txt but was never generated.

Signed-off-by: Roman Tsisyk <roman@tsisyk.com>
2025-03-06 18:24:01 +00:00
Aiden
e2f0b8b258 [strings] Generate Maltese (mt) from strings.txt
The language was present in strings.txt but was never generated.

Signed-off-by: Roman Tsisyk <roman@tsisyk.com>
2025-03-06 18:21:36 +00:00
Ernest King
840fa7d7f7 [strings] Generate Lithuanian (lt) from strings.txt
The language was present in strings.txt but was never generated.

See https://github.com/organicmaps/organicmaps/pull/5163

Signed-off-by: Roman Tsisyk <roman@tsisyk.com>
2025-03-06 18:19:43 +00:00
d7db7df723 [strings] Update Android translations for Spanish
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android/es/
Signed-off-by: Adolfo Jayme Barrientos <fitojb@ubuntu.com>
2025-03-06 04:31:01 +00:00
e8cbb31005 [strings] Update iOS translations for Spanish
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios/es/
Signed-off-by: Adolfo Jayme Barrientos <fitojb@ubuntu.com>
2025-03-06 04:31:01 +00:00
aaa40a9b77 [strings] Update Android translations for Spanish
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android/es/
Signed-off-by: Adolfo Jayme Barrientos <fitojb@ubuntu.com>
2025-03-06 04:31:01 +00:00
cd2f801fe6 [strings] Update iOS translations for Spanish
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios/es/
Signed-off-by: Adolfo Jayme Barrientos <fitojb@ubuntu.com>
2025-03-06 04:31:01 +00:00
ERYpTION
dcaca3ecb5 [strings] Update iOS translations for Danish
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios/da/
Signed-off-by: ERYpTION <eryption.ar9q2@slmail.me>
2025-03-06 04:31:01 +00:00
4421a7b6e8 [strings] Update Android translations for Spanish
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android-typestrings/es/
Signed-off-by: Anton <developing.anton@gmail.com>
2025-03-06 04:31:01 +00:00
5a1e518c3c [strings] Update Android translations for Spanish
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-typestrings/es/
Signed-off-by: Anton <developing.anton@gmail.com>
2025-03-06 04:31:01 +00:00
4a2f4a9f03 [strings] Update Android translations for Spanish (Mexico)
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android/es_MX/
Signed-off-by: Anton <developing.anton@gmail.com>
2025-03-06 04:31:01 +00:00
ERYpTION
028720a452 [strings] Update Android translations for Danish
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android/da/
Signed-off-by: ERYpTION <eryption.ar9q2@slmail.me>
2025-03-06 04:31:01 +00:00
ERYpTION
1f043478df [strings] Update iOS translations for Danish
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios/da/
Signed-off-by: ERYpTION <eryption.ar9q2@slmail.me>
2025-03-06 04:31:01 +00:00
ERYpTION
bef5ed25ca [strings] Update Android translations for Danish
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-plist/da/
Signed-off-by: ERYpTION <eryption.ar9q2@slmail.me>
2025-03-06 04:31:01 +00:00
Weblate Translation Memory
6bcc35d982 [strings] Update iOS plurals translations for German
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-plurals/de/
Signed-off-by: Weblate Translation Memory <noreply-mt-weblate-translation-memory@weblate.org>
2025-03-06 04:31:01 +00:00
MaggusK
de536b96e9 [strings] Update iOS plurals translations for German
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-plurals/de/
Signed-off-by: MaggusK <aspams@magg4u.de>
2025-03-06 04:31:01 +00:00
ERYpTION
62cb82c09f [strings] Update Android translations for Danish
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android/da/
Signed-off-by: ERYpTION <eryption.ar9q2@slmail.me>
2025-03-06 04:31:01 +00:00
Fjuro
a773bf7cc6 [strings] Update Android translations for Czech
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android/cs/
Signed-off-by: Fjuro <fjuro@users.noreply.hosted.weblate.org>
2025-03-06 04:31:01 +00:00
Weblate Translation Memory
8c371fb509 [strings] Update Android translations for Belarusian
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android/be/
Signed-off-by: Weblate Translation Memory <noreply-mt-weblate-translation-memory@weblate.org>
2025-03-06 04:31:01 +00:00
Dmitry Gribenchuk
a887d930f7 [strings] Update Android translations for Belarusian
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android/be/
Signed-off-by: Dmitry Gribenchuk <dmitrygribenchuk@gmail.com>
2025-03-06 04:31:01 +00:00
ERYpTION
e0a8a1f1f8
[strings] Update iOS translations for Danish
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios/da/
Signed-off-by: ERYpTION <eryption.ar9q2@slmail.me>
2025-03-05 22:24:15 +01:00
Fjuro
1d69dfe8ce
[strings] Update iOS translations for Czech
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios/cs/
Signed-off-by: Fjuro <fjuro@users.noreply.hosted.weblate.org>
2025-03-05 22:24:15 +01:00
Fjuro
8b93625010
[strings] Update Android translations for Czech
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android-typestrings/cs/
Signed-off-by: Fjuro <fjuro@users.noreply.hosted.weblate.org>
2025-03-05 21:27:14 +01:00
Fjuro
4fe769be75
[strings] Update Android translations for Czech
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-typestrings/cs/
Signed-off-by: Fjuro <fjuro@users.noreply.hosted.weblate.org>
2025-03-05 21:27:14 +01:00
Weblate Translation Memory
f3c472c23a
[strings] Update Android translations for Danish
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android/da/
Signed-off-by: Weblate Translation Memory <noreply-mt-weblate-translation-memory@weblate.org>
2025-03-05 21:27:13 +01:00
ERYpTION
fcb2a3af29
[strings] Update Android translations for Danish
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android/da/
Signed-off-by: ERYpTION <eryption.ar9q2@slmail.me>
2025-03-05 21:27:13 +01:00
Fjuro
0143ac3538
[strings] Update Android translations for Czech
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android/cs/
Signed-off-by: Fjuro <fjuro@users.noreply.hosted.weblate.org>
2025-03-05 21:27:13 +01:00
Fjuro
b6e9181c98
[strings] Update iOS translations for Czech
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios/cs/
Signed-off-by: Fjuro <fjuro@users.noreply.hosted.weblate.org>
2025-03-05 21:27:12 +01:00
Fjuro
20d608f850 [strings] Update Android translations for Czech
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android-typestrings/cs/
Signed-off-by: Fjuro <fjuro@users.noreply.hosted.weblate.org>
2025-03-05 20:05:49 +00:00
Fjuro
dccf7b97fe [strings] Update Android translations for Czech
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-typestrings/cs/
Signed-off-by: Fjuro <fjuro@users.noreply.hosted.weblate.org>
2025-03-05 20:05:49 +00:00
55a903903b [strings] Update Android translations for Spanish
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android/es/
Signed-off-by: muralito <muralito@montevideo.com.uy>
2025-03-05 20:05:49 +00:00
c29584acbe [strings] Update Android translations for Spanish
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android/es/
Signed-off-by: muralito <muralito@montevideo.com.uy>
2025-03-05 20:05:49 +00:00
Jaime Marquínez Ferrándiz
7ae108ed6e [strings] Update Android translations for Spanish
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android/es/
Signed-off-by: Jaime Marquínez Ferrándiz <jaime.marquinez.ferrandiz@fastmail.net>
2025-03-05 20:05:49 +00:00
Jaime Marquínez Ferrándiz
d3713cd8ca [strings] Update iOS translations for Spanish
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios/es/
Signed-off-by: Jaime Marquínez Ferrándiz <jaime.marquinez.ferrandiz@fastmail.net>
2025-03-05 20:05:49 +00:00
2b29b484a5 [strings] Update iOS translations for Spanish
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios/es/
Signed-off-by: muralito <muralito@montevideo.com.uy>
2025-03-05 20:05:49 +00:00
7e79f90e7f [strings] Update Android translations for Spanish
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android/es/
Signed-off-by: muralito <muralito@montevideo.com.uy>
2025-03-05 20:05:49 +00:00
Andi Chandler
e7c16d75ae [strings] Update Android translations for English (United Kingdom)
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android-typestrings/en_GB/
Signed-off-by: Andi Chandler <andi@gowling.com>
2025-03-05 20:05:49 +00:00
Jaime Marquínez Ferrándiz
51e47b12d2 [strings] Update iOS plurals translations for Spanish
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-plurals/es/
Signed-off-by: Jaime Marquínez Ferrándiz <jaime.marquinez.ferrandiz@fastmail.net>
2025-03-05 20:05:49 +00:00
Andi Chandler
033cc84649 [strings] Update iOS plurals translations for English (United Kingdom)
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-plurals/en_GB/
Signed-off-by: Andi Chandler <andi@gowling.com>
2025-03-05 20:05:49 +00:00
Matheus Gomes Correia
f4862bbeff [strings] Update Android translations for Portuguese (Brazil)
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android/pt_BR/
Signed-off-by: Matheus Gomes Correia <matheus.gomes03@hotmail.com>
2025-03-05 20:05:49 +00:00
Weblate Translation Memory
22577a0a57 [strings] Update Android translations for Spanish
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android/es/
Signed-off-by: Weblate Translation Memory <noreply-mt-weblate-translation-memory@weblate.org>
2025-03-05 20:05:49 +00:00
73fd45af68 [strings] Update Android translations for Spanish
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android/es/
Signed-off-by: muralito <muralito@montevideo.com.uy>
2025-03-05 20:05:49 +00:00
Matheus Gomes Correia
d9e6ebf809 [strings] Update iOS translations for Portuguese (Brazil)
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios/pt_BR/
Signed-off-by: Matheus Gomes Correia <matheus.gomes03@hotmail.com>
2025-03-05 20:05:49 +00:00
028c4b28a3 [strings] Update iOS translations for Spanish
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios/es/
Signed-off-by: muralito <muralito@montevideo.com.uy>
2025-03-05 20:05:49 +00:00
cf06b21e53 [store] Update iOS translation files
Updated by "Remove blank strings" hook in Weblate.

Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios/
Signed-off-by: Roman Tsisyk <roman@tsisyk.com>
2025-03-05 18:11:07 +00:00
ce97f195d7 [store] Update iOS translation files
Updated by "Remove blank strings" hook in Weblate.

Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios/
Signed-off-by: Roman Tsisyk <roman@tsisyk.com>
2025-03-05 18:11:07 +00:00
d3a4e8e7b2 [store] Update iOS translation files
Updated by "Remove blank strings" hook in Weblate.

Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios/
Signed-off-by: Roman Tsisyk <roman@tsisyk.com>
2025-03-05 18:11:07 +00:00
91b607276f [store] Update iOS translation files
Updated by "Remove blank strings" hook in Weblate.

Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios/
Signed-off-by: Roman Tsisyk <roman@tsisyk.com>
2025-03-05 18:11:07 +00:00
Fjuro
7f49ad21fe [strings] Update Android translations for Czech
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android-typestrings/cs/
Signed-off-by: Fjuro <fjuro@users.noreply.hosted.weblate.org>
2025-03-05 18:11:07 +00:00
Fjuro
bd1b2f5c93 [strings] Update Android translations for Czech
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-typestrings/cs/
Signed-off-by: Fjuro <fjuro@users.noreply.hosted.weblate.org>
2025-03-05 18:11:07 +00:00
Fjuro
538dd1bf1e [strings] Update Android translations for Czech
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android/cs/
Signed-off-by: Fjuro <fjuro@users.noreply.hosted.weblate.org>
2025-03-05 18:11:07 +00:00
Fjuro
c6efc5dba1 [strings] Update iOS translations for Czech
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios/cs/
Signed-off-by: Fjuro <fjuro@users.noreply.hosted.weblate.org>
2025-03-05 18:11:07 +00:00
58c5d87455 [store] Update iOS translation files
Updated by "Remove blank strings" hook in Weblate.

Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios/
Signed-off-by: Roman Tsisyk <roman@tsisyk.com>
2025-03-05 18:07:33 +00:00
f3acc898cd [strings] Update Android translations for Serbian
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-typestrings/sr/
Signed-off-by: ZeljkoBG <zeljko.velickovic@gmail.com>
2025-03-05 18:07:33 +00:00
53a39be6f6 [strings] Update Android translations for Serbian
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android-typestrings/sr/
Signed-off-by: ZeljkoBG <zeljko.velickovic@gmail.com>
2025-03-05 18:07:33 +00:00
15a4a80583 [strings] Update Android translations for Russian
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-typestrings/ru/
Signed-off-by: Konstantin <konstantin.pastbin@gmail.com>
2025-03-05 18:07:33 +00:00
d18843f344 [strings] Update Android translations for Russian
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android-typestrings/ru/
Signed-off-by: Konstantin <konstantin.pastbin@gmail.com>
2025-03-05 18:07:33 +00:00
Priit Jõerüüt
e7d97f467b [strings] Update Android translations for Estonian
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android-typestrings/et/
Signed-off-by: Priit Jõerüüt <hwlate@joeruut.com>
2025-03-05 18:07:33 +00:00
Priit Jõerüüt
2759fed166 [strings] Update Android translations for Estonian
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-typestrings/et/
Signed-off-by: Priit Jõerüüt <hwlate@joeruut.com>
2025-03-05 18:07:33 +00:00
423c54d21c [strings] Update Android translations for Serbian
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-plist/sr/
Signed-off-by: ZeljkoBG <zeljko.velickovic@gmail.com>
2025-03-05 18:07:33 +00:00
51e565feed [strings] Update Sounds translations for Serbian
Translate-URL: https://hosted.weblate.org/projects/organicmaps/tts/sr/
Signed-off-by: ZeljkoBG <zeljko.velickovic@gmail.com>
2025-03-05 18:07:33 +00:00
Priit Jõerüüt
510980c2f2 Translated using Weblate (Estonian)
Currently translated at 100.0% (2552 of 2552 strings)

Translation: OrganicMaps/WIP: Country names for downloader
Translate-URL: https://hosted.weblate.org/projects/organicmaps/countries/et/
2025-03-05 18:07:33 +00:00
d16cceb9c2 [strings] Update iOS plurals translations for Serbian
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-plurals/sr/
Signed-off-by: ZeljkoBG <zeljko.velickovic@gmail.com>
2025-03-05 18:07:33 +00:00
2f1917dc04 [strings] Update iOS translations for Serbian
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios/sr/
Signed-off-by: ZeljkoBG <zeljko.velickovic@gmail.com>
2025-03-05 18:07:33 +00:00
8d05a39efe [strings] Update Android translations for Serbian
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android/sr/
Signed-off-by: ZeljkoBG <zeljko.velickovic@gmail.com>
2025-03-05 18:07:33 +00:00
6b2f8db1d7 [store] Update Android translation files
Updated by "Remove blank strings" hook in Weblate.

Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-plist/
Signed-off-by: Roman Tsisyk <roman@tsisyk.com>
2025-03-05 18:07:33 +00:00
Priit Jõerüüt
0c0af7b029 [strings] Update Android translations for Estonian
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android-typestrings/et/
Signed-off-by: Priit Jõerüüt <hwlate@joeruut.com>
2025-03-05 18:07:33 +00:00
Priit Jõerüüt
e9f32a77ea [strings] Update Android translations for Estonian
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-typestrings/et/
Signed-off-by: Priit Jõerüüt <hwlate@joeruut.com>
2025-03-05 18:07:33 +00:00
Priit Jõerüüt
334752facf [strings] Update Android translations for Estonian
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-plist/et/
Signed-off-by: Priit Jõerüüt <hwlate@joeruut.com>
2025-03-05 18:07:33 +00:00
Priit Jõerüüt
4d9514b510 [strings] Update iOS plurals translations for Estonian
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-plurals/et/
Signed-off-by: Priit Jõerüüt <hwlate@joeruut.com>
2025-03-05 18:07:33 +00:00
Priit Jõerüüt
e739a2f5c3 [strings] Update Sounds translations for Estonian
Translate-URL: https://hosted.weblate.org/projects/organicmaps/tts/et/
Signed-off-by: Priit Jõerüüt <hwlate@joeruut.com>
2025-03-05 18:07:33 +00:00
Priit Jõerüüt
8db2a0136e [strings] Update iOS plurals translations for Estonian
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-plurals/et/
Signed-off-by: Priit Jõerüüt <hwlate@joeruut.com>
2025-03-05 18:07:33 +00:00
Priit Jõerüüt
baed033cf0 [strings] Update Sounds translations for Estonian
Translate-URL: https://hosted.weblate.org/projects/organicmaps/tts/et/
Signed-off-by: Priit Jõerüüt <hwlate@joeruut.com>
2025-03-05 18:07:33 +00:00
7d58b65a11 [store] Update Sounds translation files
Updated by "Remove blank strings" hook in Weblate.

Translate-URL: https://hosted.weblate.org/projects/organicmaps/tts/
Signed-off-by: Roman Tsisyk <roman@tsisyk.com>
2025-03-05 18:07:33 +00:00
Priit Jõerüüt
ceba407373 [strings] Update Sounds translations for Estonian
Translate-URL: https://hosted.weblate.org/projects/organicmaps/tts/et/
Signed-off-by: Priit Jõerüüt <hwlate@joeruut.com>
2025-03-05 18:07:33 +00:00
Priit Jõerüüt
2c766f64ba [strings] Add Sounds translations for Estonian
Translate-URL: https://hosted.weblate.org/projects/organicmaps/tts/et/
Signed-off-by: Priit Jõerüüt <hwlate@joeruut.com>
2025-03-05 18:07:33 +00:00
8e885b38e3 [store] Update Android translation files
Updated by "Remove blank strings" hook in Weblate.

Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-typestrings/
Signed-off-by: Roman Tsisyk <roman@tsisyk.com>
2025-03-05 18:07:33 +00:00
7a149d3d22 [strings] Update Android translations for Russian
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-typestrings/ru/
Signed-off-by: Konstantin <konstantin.pastbin@gmail.com>
2025-03-05 18:07:33 +00:00
fa3b4cb661 [strings] Update Android translations for Russian
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android-typestrings/ru/
Signed-off-by: Konstantin <konstantin.pastbin@gmail.com>
2025-03-05 18:07:33 +00:00
27bd379ac3 [store] Update Android translation files
Updated by "Remove blank strings" hook in Weblate.

Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-typestrings/
Signed-off-by: Roman Tsisyk <roman@tsisyk.com>
2025-03-05 18:07:33 +00:00
d93eba8cc7 [strings] Update Android translations for English
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android-typestrings/en/
Signed-off-by: Konstantin <konstantin.pastbin@gmail.com>
2025-03-05 18:07:33 +00:00
10ffea348d [store] Update Android translation files
Updated by "Remove blank strings" hook in Weblate.

Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-typestrings/
Signed-off-by: Roman Tsisyk <roman@tsisyk.com>
2025-03-05 18:07:33 +00:00
6ef933de5b [strings] Update Android translations for Serbian
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-typestrings/sr/
Signed-off-by: ZeljkoBG <zeljko.velickovic@gmail.com>
2025-03-05 18:07:33 +00:00
f4be439d29 [strings] Update Android translations for Serbian
Translate-URL: https://hosted.weblate.org/projects/organicmaps/android-typestrings/sr/
Signed-off-by: ZeljkoBG <zeljko.velickovic@gmail.com>
2025-03-05 18:07:33 +00:00
Fjuro
90b8ed428f [strings] Update iOS plurals translations for Czech
Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-plurals/cs/
Signed-off-by: Fjuro <fjuro@users.noreply.hosted.weblate.org>
2025-03-05 18:07:33 +00:00
178bdf9f27 [store] Update Android translation files
Updated by "Remove blank strings" hook in Weblate.

Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-typestrings/
Signed-off-by: Roman Tsisyk <roman@tsisyk.com>
2025-03-05 18:07:33 +00:00
150b83b677 [store] Update Android translation files
Updated by "Remove blank strings" hook in Weblate.

Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-plist/
Signed-off-by: Roman Tsisyk <roman@tsisyk.com>
2025-03-05 18:07:33 +00:00
6022a07532 [store] Update iOS translation files
Updated by "Remove blank strings" hook in Weblate.

Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios/
Signed-off-by: Roman Tsisyk <roman@tsisyk.com>
2025-03-05 18:07:33 +00:00
eb5a4916ae [store] Update iOS translation files
Updated by "Remove blank strings" hook in Weblate.

Translate-URL: https://hosted.weblate.org/projects/organicmaps/ios-plurals/
Signed-off-by: Roman Tsisyk <roman@tsisyk.com>
2025-03-05 18:07:33 +00:00
00548ab8cb [strings] Remove old strings.txt
No longer used.

Signed-off-by: Roman Tsisyk <roman@tsisyk.com>
2025-03-05 18:07:24 +00:00
6e37398cf1 [strings] Switch to Weblate
https://github.com/orgs/organicmaps/discussions/4515

Weblate works fine. There is no longer a need to maintain a
homegrown, non-standard translation toolchain.

Categories are not in Weblate yet, but they weren't supported by
the previous toolkit too. This issue can be addressed later.

Signed-off-by: Roman Tsisyk <roman@tsisyk.com>
2025-03-05 18:07:24 +00:00
058644ecef [strings] Regenerated after extracting types strings
Signed-off-by: Roman Tsisyk <roman@tsisyk.com>
2025-03-05 09:07:52 +00:00
173b7e4515 [strings] [ios] move types from Localizable to LocalizableTypes
1. add separate `LocalizableTypes.strings` file for the types
2. patch the `GetLocalizedTypeName` cpp function to fetch the value from the proper table

Signed-off-by: Kiryl Kaveryn <kirylkaveryn@gmail.com>
2025-03-05 09:07:14 +00:00
ac1cbbfa2a [strings] Extract types strings into individual files
Signed-off-by: Roman Tsisyk <roman@tsisyk.com>
2025-03-05 09:07:14 +00:00
3b3e063ef9 [ios] fix search header radius small for iphone
Signed-off-by: Kiryl Kaveryn <kirylkaveryn@gmail.com>
2025-03-05 08:17:53 +00:00
6eab4f9537 [drape] Fix initialization of struct
Signed-off-by: Gonzalo Pesquero <gpesquero@yahoo.es>
2025-03-04 19:09:29 -03:00
1753a8bc87 [drape] Remove Vulkan warnings
Signed-off-by: Gonzalo Pesquero <gpesquero@yahoo.es>
2025-03-03 17:42:46 -03:00
d21800ec25 [github] Add leading slash (root) to all dirs
Signed-off-by: Konstantin Pastbin <konstantin.pastbin@gmail.com>
2025-03-02 16:07:26 +00:00
62778dbfbe [android] Fix crash upon deleting a POI via the Editor
Resolves #10211

Signed-off-by: savsch <119003089+savsch@users.noreply.github.com>
2025-03-02 18:47:15 +03:00
540bd70d96 Implements track recording widget on screen
Signed-off-by: kavikhalique <kavikhalique3@gmail.com>
2025-03-02 13:26:40 +00:00
c23d782d6f [android] Add manage route functionality
Signed-off-by: Gonzalo Pesquero <gpesquero@yahoo.es>
2025-03-01 13:06:25 +00:00
c7b2b7d136 [strings] Add manage route strings to Android
Signed-off-by: Gonzalo Pesquero <gpesquero@yahoo.es>
2025-03-01 13:06:25 +00:00
d2f8a03468 [android] Add route point icons
Signed-off-by: Gonzalo Pesquero <gpesquero@yahoo.es>
2025-03-01 13:06:25 +00:00
5344ed5940 [strings] Regenerated
Signed-off-by: Roman Tsisyk <roman@tsisyk.com>
2025-03-01 09:14:48 +00:00
zyphlar
97fc3606f9 [strings] Regenerated
Signed-off-by: zyphlar <zyphlar@users.noreply.github.com>
2025-03-01 09:01:15 +00:00
zyphlar
13071a609c Add recalculating TTS announcement
Signed-off-by: zyphlar <zyphlar@users.noreply.github.com>
2025-03-01 09:01:15 +00:00
b14939e770 [android] Don't set subway routing mode when layer active
Signed-off-by: Harry Bond <me@hbond.xyz>
2025-03-01 05:58:19 +03:00
32bf3a3e9a [editor] Make new POIs editable
Signed-off-by: Konstantin Pastbin <konstantin.pastbin@gmail.com>
2025-03-01 05:29:12 +03:00
147e12360c [github] Do not require POI PR reviews from design and data teams
Signed-off-by: Konstantin Pastbin <konstantin.pastbin@gmail.com>
2025-02-28 20:02:16 +00:00
7bfbb517cf [Android] hide UI in big direction mode
Signed-off-by: Harry Bond <me@hbond.xyz>
2025-02-28 18:48:25 +00:00
FinixFighter
33ca7570ad [categories] Update categories.txt
Added italian search term for car repair shop.
"Meccanico" is the most commonly used term for this kind of shops.

Signed-off-by: FinixFighter <19877271+FinixFighter@users.noreply.github.com>
2025-02-28 18:52:17 +03:00
644b9da2b1 [strings] Regenerated
Signed-off-by: Roman Tsisyk <roman@tsisyk.com>
2025-02-28 13:59:33 +00:00
c52a3e3c09 [strings] Update plurals from ChatGPT
Signed-off-by: Roman Tsisyk <roman@tsisyk.com>
2025-02-28 13:59:09 +00:00
44f3475b90 [strings] Regenerated
Signed-off-by: Roman Tsisyk <roman@tsisyk.com>
2025-02-28 13:56:49 +00:00
c5019f0252 [strings] Remove brands_strings.txt
This file isn't referenced anywhere. It's just an artifact.

Signed-off-by: Roman Tsisyk <roman@tsisyk.com>
2025-02-28 13:56:03 +00:00
Parneet Singh
018dfb0cbc [android] Remove unused ProgressDialogFragment file (#10354)
Signed-off-by: parneet-guraya <gurayaparneet@gmail.com>
2025-02-28 13:56:03 +00:00
Alexey Krasilnikov
a11c266584 Start DownloaderService when map update is requested also
Signed-off-by: Alexey Krasilnikov <alexey@krasilnikov.me>
2025-02-28 13:56:03 +00:00
Alexey Krasilnikov
08b9e407cb Decrease download notifications importance to avoid disturbing sounds
Signed-off-by: Alexey Krasilnikov <alexey@krasilnikov.me>
2025-02-28 13:56:03 +00:00
2c002eb08b [android] Fix SpeedLimitView
Signed-off-by: Andrei Shkrob <github@shkrob.dev>
2025-02-28 13:56:03 +00:00
c96d873fa8 [map] return the ElevationInfo for the current track recording
Signed-off-by: Kiryl Kaveryn <kirylkaveryn@gmail.com>
2025-02-28 13:56:03 +00:00
Boris Verkhovskiy
40e1e63315 Proofread English strings 2025-02-28 13:56:03 +00:00
a9d85463ab [drape] Fix disappearing building POIs when in perspective
Signed-off-by: Konstantin Pastbin <konstantin.pastbin@gmail.com>
2025-02-28 13:56:02 +00:00
55951a28be [ios] fix search result selection index after filtering the suggestions
Signed-off-by: Kiryl Kaveryn <kirylkaveryn@gmail.com>
2025-02-28 13:56:02 +00:00
52bbb9c28a [strings] Removes unused strings
- Ran clean_strings_txt.py
Signed-off-by: hemanggs <hemangmanhas@gmail.com>
2025-02-26 12:54:54 +03:00
a4067bc128 [android] Alerts users when precise permission not given
- Added strings to strings.txt
- overloaded OpenUri to support system Action
- Translated strings with DeepL api

Signed-off-by: hemanggs <hemangmanhas@gmail.com>
2025-02-26 12:54:54 +03:00
806ef7e749 Status Icon Fix
Signed-off-by: hemanggs <hemangmanhas@gmail.com>
2025-02-26 10:30:20 +03:00
parneet-guraya
57e72f93bc Use new result apis
Signed-off-by: parneet-guraya <gurayaparneet@gmail.com>
2025-02-26 10:28:28 +03:00
d6d7a33bdf [styles] Regenerate for power towers and lines
Signed-off-by: Konstantin Pastbin <konstantin.pastbin@gmail.com>
2025-02-25 21:48:40 +03:00
6b458d4f65 [strings] Regenerate for power towers and line
Signed-off-by: Konstantin Pastbin <konstantin.pastbin@gmail.com>
2025-02-25 21:48:40 +03:00
e49e836f42 [classif] Add man_made=utility_pole
Signed-off-by: Konstantin Pastbin <konstantin.pastbin@gmail.com>
2025-02-25 21:48:40 +03:00
d281139fd6 [styles] Add minor power lines and poles
Signed-off-by: Konstantin Pastbin <konstantin.pastbin@gmail.com>
2025-02-25 21:48:40 +03:00
80413f97e1 [styles][outdoor] Increase visibility of power lines and towers
Signed-off-by: Konstantin Pastbin <konstantin.pastbin@gmail.com>
2025-02-25 21:48:40 +03:00
4f6ee70bf8 [symbols] Regenerate for amenity-motorcycle_rental
Signed-off-by: Konstantin Pastbin <konstantin.pastbin@gmail.com>
2025-02-25 18:29:22 +03:00
3925b423a1 [styles] Regenerate for amenity-motorcycle_rental
Signed-off-by: Konstantin Pastbin <konstantin.pastbin@gmail.com>
2025-02-25 18:29:22 +03:00
e767b0f454 [strings] Regenerate for amenity-motorcycle_rental
Signed-off-by: Konstantin Pastbin <konstantin.pastbin@gmail.com>
2025-02-25 18:29:22 +03:00
cc216c4895 [classif] Add amenity-motorcycle_rental
Signed-off-by: Konstantin Pastbin <konstantin.pastbin@gmail.com>
2025-02-25 18:29:22 +03:00
4b15a12893 Revert "[ios] Use today's commits count and two numbers from commit hash in the build number"
This reverts commit ee36eb8d12.
2025-02-25 12:18:28 +00:00
8dfd9a789c [map] show Track PP after TrackRecording saving
Signed-off-by: Kiryl Kaveryn <kirylkaveryn@gmail.com>
2025-02-25 12:18:06 +00:00
377d10de8c [ios] remove outdated code related to the search
the old MWMSearch... screen is not used anymore

Signed-off-by: Kiryl Kaveryn <kirylkaveryn@gmail.com>
2025-02-25 12:17:33 +00:00
f9fb0e8d7f [ios] replace MWMSearchManager with new SearchOnMapManager
- fix layout of the place page container (configure it programatiacally)
- use the new modal seearch VC everywhere
-

Signed-off-by: Kiryl Kaveryn <kirylkaveryn@gmail.com>
2025-02-25 12:17:33 +00:00
fbc1ebd84d [ios] remove setSearchOnMap from the MWMSearch
because the new search should always works as iPad

Signed-off-by: Kiryl Kaveryn <kirylkaveryn@gmail.com>
2025-02-25 12:17:33 +00:00
5db61f0498 [ios] implement modal search screen SearchOnMap
Signed-off-by: Kiryl Kaveryn <kirylkaveryn@gmail.com>
2025-02-25 12:17:33 +00:00
4eb7bf3f73 [ios] add searchHeader style
Signed-off-by: Kiryl Kaveryn <kirylkaveryn@gmail.com>
2025-02-25 12:17:33 +00:00
fcaad4d98e [ios] get the current Side buttons available size
This size is used to update the side buttons during the modally search screen dragging

Signed-off-by: Kiryl Kaveryn <kirylkaveryn@gmail.com>
2025-02-25 12:17:33 +00:00
f42083b8c9 [ios] prevent search marks hiding when the search result is selected
Signed-off-by: Kiryl Kaveryn <kirylkaveryn@gmail.com>
2025-02-25 12:17:33 +00:00
c666a68b37 [ios] rename IPAD func to isIPad and make internal visibility
it may be used in different situations

Signed-off-by: Kiryl Kaveryn <kirylkaveryn@gmail.com>
2025-02-25 12:17:33 +00:00
e53dcf7716 [ios] show current searching results on the map
Signed-off-by: Kiryl Kaveryn <kirylkaveryn@gmail.com>
2025-02-25 12:17:33 +00:00
ca2d888744 [ios] add getResults to the MWMSearch to fetch the full results list
Signed-off-by: Kiryl Kaveryn <kirylkaveryn@gmail.com>
2025-02-25 12:17:33 +00:00
Alexey Krasilnikov
cd832b94b6 Add foreground service to indicate about ongoing downloads
Signed-off-by: Alexey Krasilnikov <alexey@krasilnikov.me>
2025-02-25 11:14:56 +00:00
1cbe25e5bc [ios] fix route planning reloading ui bug
The bottom route status view sometimes jumps from the top to the bottom during the 1st rendering. It happens because the view's frame is not calculated on the isVisible because the view is not init/deinit multiple times during the route building state changing. The bottom view now only changes its visibility and constraints, not the init/deinit on every update.

Signed-off-by: Kiryl Kaveryn <kirylkaveryn@gmail.com>
2025-02-25 10:58:14 +00:00
db4371e2c3 [ios] refactor search screen cells and tab bar
- remove xibs where possible
- update cells style (fonts, colors, icons)

Signed-off-by: Kiryl Kaveryn <kirylkaveryn@gmail.com>
2025-02-25 10:53:35 +00:00
varad64
9e47a49ec0 Use SharedPreferences to restore the last selected search tab on app restart.
Code refactor to change LastSelectedTab to LastSearchedTab.
Signed-off-by: varad64 <varad23711@users.noreply.github.com>

Code refactor to change LastSelectedTab to LastSearchedTab
2025-02-25 13:43:18 +03:00
83994a247e [ios] rename finishShareCategory to finishSharing
Signed-off-by: Kiryl Kaveryn <kirylkaveryn@gmail.com>
2025-02-25 10:18:51 +00:00
98ef2d61fb [ios] export single track from the place page as kml/gpx
Signed-off-by: Kiryl Kaveryn <kirylkaveryn@gmail.com>
2025-02-25 10:18:51 +00:00
524f3fe358 [ios] show add stop button to the selected track point during the nav mode
Signed-off-by: Kiryl Kaveryn <kirylkaveryn@gmail.com>
2025-02-25 09:42:48 +00:00
34510b8f1c [map] clear the gps tracker on save
The prev solution was to clean up the tracker points before the new track recording starting.
It was not fully correct solution because:
1. it causes the bug, when if we starts a recording, the `TrackRecordingUpdateHandler` is called by subscribers but receives the old `track info` from the previous track. It happens because the starting is an async call and it cleans up the gps collection a little bit later.
2. when the user finishes the track recording the collection is not properly cleared. The data will stay in the memory `forewer` until the next recording is started. And this data will be recovered on the next app launch too. There are no reason to store all the recorded data in memory until the new recording begins. This approach was Ok for the `previous path` feature (removed) but not for the TR. The data lifecycle for the both feature should be handled separately.

Signed-off-by: Kiryl Kaveryn <kirylkaveryn@gmail.com>
2025-02-25 09:39:35 +00:00
0687642095 [ios] fix memory leak in the PlacePageInfoViewController
Signed-off-by: Kiryl Kaveryn <kirylkaveryn@gmail.com>
2025-02-25 09:38:42 +00:00
b9d0138cd1 [styles] Regenerate for pathtexts
Signed-off-by: Konstantin Pastbin <konstantin.pastbin@gmail.com>
2025-02-25 07:08:55 +03:00
cf7a71b590 [styles] Add pathtexts to power lines
Signed-off-by: Konstantin Pastbin <konstantin.pastbin@gmail.com>
2025-02-25 07:08:55 +03:00
1ea96c4069 [styles] Add captions to linear cliffs, city walls, embankments
Signed-off-by: Konstantin Pastbin <konstantin.pastbin@gmail.com>
2025-02-25 07:08:55 +03:00
5ffd95a491 [ios] replace usage of cpp search::Result with objc SearchResult in UI
Signed-off-by: Kiryl Kaveryn <kirylkaveryn@gmail.com>
2025-02-24 11:25:14 +04:00
69953c2b12 [ios] remove unused search::ProductInfo from the search
Signed-off-by: Kiryl Kaveryn <kirylkaveryn@gmail.com>
2025-02-24 11:25:14 +04:00
KOIZUMI Satoru
868b538435 [strings] Regenerated
Signed-off-by: KOIZUMI Satoru <koizumistr@minos.ocn.ne.jp>
2025-02-24 08:10:15 +03:00
KOIZUMI Satoru
ca29e0b563 [strings] fix 'sort' in Japanese
Signed-off-by: KOIZUMI Satoru <koizumistr@minos.ocn.ne.jp>
2025-02-24 08:10:15 +03:00
25a6c1a5b1 [tools] Remove outdated scripts
Signed-off-by: Andrei Shkrob <github@shkrob.dev>
2025-02-23 20:09:53 +00:00
fced7be257 [indexer] Remove android tests
Signed-off-by: Andrei Shkrob <github@shkrob.dev>
2025-02-23 20:07:14 +00:00
Dmitry Gribenchuk
eacb3bbfa2 [strings] Regenerate
Signed-off-by: Dmitry Gribenchuk <dmitrygribenchuk@gmail.com>
2025-02-23 19:45:50 +03:00
Dmitry Gribenchuk
b2464b2d31 [strings] Regenerate
Signed-off-by: Dmitry Gribenchuk <dmitrygribenchuk@gmail.com>
2025-02-23 19:45:50 +03:00
462b73ef5a [styles] Regenerate drules
Signed-off-by: hemanggs <hemangmanhas@gmail.com>
2025-02-23 17:33:00 +03:00
1e0065ab4d [styles] Updates zoom of all priority 500 shops to z18
- Made all z18 shops consistent z18

Signed-off-by: Hemang Manhas <hemangmanhas@gmail.com>
2025-02-23 17:33:00 +03:00
4a82b560bd [styles] Regenerate symbols
Signed-off-by: hemanggs <hemangmanhas@gmail.com>
2025-02-23 17:31:56 +03:00
8df6183ba7 [android] Fixes icon colors
- Reverts opening hours tint that was wrongly set to white which is not visible in light mode in the commit 5841d0f .

- Sets color of hearing_aids icon same as  other shop icons

Signed-off-by: hemanggs <hemangmanhas@gmail.com>
2025-02-23 17:31:56 +03:00
9c7438cb69 [android] RoutingErrorDialogFragment dialog fix
Signed-off-by: hemanggs <hemangmanhas@gmail.com>
2025-02-23 17:31:56 +03:00
645f17c441 [android] Reorders stop track recording dialog box buttons
Signed-off-by: hemanggs <hemangmanhas@gmail.com>
2025-02-23 17:31:56 +03:00
234dfb6d57 [android] Fixes downloader message UI
- Fixes #8520

Signed-off-by: hemanggs <hemangmanhas@gmail.com>
2025-02-23 17:31:56 +03:00
f5e61c7f35 [github] Don't require Design team to review each map icon change
Signed-off-by: Konstantin Pastbin <konstantin.pastbin@gmail.com>
2025-02-23 16:27:07 +03:00
b0f96bf094 [strings] Fix plurals for pl, ru, uk
Signed-off-by: Roman Tsisyk <roman@tsisyk.com>
2025-02-23 12:41:09 +00:00
Dwayne Bailey
09127e75c8 [strings] Remove double spaces
These are not used in the target formats XML swallows spaces,
translators should use unicode nbsp and such if they truly want two
spaces.

Primarily this prevents roundtrip noise when we extract the string to
Twine and drop double spaces.

Signed-off-by: Roman Tsisyk <roman@tsisyk.com>
2025-02-23 12:05:50 +00:00
Dwayne Bailey
e9326e99e4 [strings] Fix wrapped localisations
These lines got merged mistakenly in commit 66dba98d01 and 72c63f88a9

Signed-off-by: Roman Tsisyk <roman@tsisyk.com>
2025-02-23 12:05:44 +00:00
Dwayne Bailey
ce09fe33d4 [strings] Reorder twine metadata
Signed-off-by: Roman Tsisyk <roman@tsisyk.com>
2025-02-23 12:05:36 +00:00
fa52e1b3d4 [strings] Decrease discrepancies with Weblate
Signed-off-by: Roman Tsisyk <roman@tsisyk.com>
2025-02-23 11:40:52 +00:00
Alexander Borsuk
1a3a527af3 [strings] Regenerated PP notes (html or text)
Signed-off-by: Alexander Borsuk <me@alex.bio>
2025-02-23 12:18:31 +03:00
Alexander Borsuk
af8af7a8a5 [android] Removed unused resources XML
Signed-off-by: Alexander Borsuk <me@alex.bio>
2025-02-23 12:18:31 +03:00
Alexander Borsuk
313664dc1c [strings] Use the same text hint mentioning HTML in all relevant places
Users do not know that HTML can be used in bookmarks descriptions.
Previously this hint was visible on iOS.
There's also no need to duplicate the text in several places.

Signed-off-by: Alexander Borsuk <me@alex.bio>
2025-02-23 12:18:31 +03:00
cyber-toad
75e5cd9168 [gpx] Use cdata for name field in gpx export
Signed-off-by: cyber-toad <the.cyber.toad@proton.me>
2025-02-23 08:57:17 +03:00
9240602fa5 [android] Retain bookmark color/category edits across screen rotations
Resolves #2419

Signed-off-by: savsch <119003089+savsch@users.noreply.github.com>
2025-02-22 15:18:04 +03:00
cyber-toad
04eb9642bb [bookmarks] Share single track
Signed-off-by: cyber-toad <the.cyber.toad@proton.me>
2025-02-22 14:10:51 +03:00
83df1e5004 [map] review fixes
Signed-off-by: Kiryl Kaveryn <kirylkaveryn@gmail.com>
2025-02-21 14:38:11 -03:00
8905269239 [android] fix compile warnings
Signed-off-by: Kiryl Kaveryn <kirylkaveryn@gmail.com>
2025-02-21 14:38:11 -03:00
1783c90714 [map] move the track stats calc logic from ElevationInfo to the TrackStatistics
Signed-off-by: Kiryl Kaveryn <kirylkaveryn@gmail.com>
2025-02-21 14:38:11 -03:00
Dmitry Gribenchuk
bc6a9e4804 [strings] Regenerate
Signed-off-by: Dmitry Gribenchuk <dmitrygribenchuk@gmail.com>
2025-02-21 09:05:17 +03:00
KOIZUMI Satoru
2331171850 [strings] fix Login TEXT in Japanese
Signed-off-by: KOIZUMI Satoru <koizumistr@minos.ocn.ne.jp>
2025-02-20 13:59:43 +03:00
cyber-toad
cb6af94e12 [bookmarks] Remove unused code from BookmarkManager
Signed-off-by: cyber-toad <the.cyber.toad@proton.me>
2025-02-20 12:41:28 +04:00
Ferenc Géczi
9678e0c491 Add 2025.02.17 release entry to metainfo.xml
Signed-off-by: Ferenc Géczi <ferenc.gm@gmail.com>
2025-02-18 13:27:42 +03:00
Viktor Govako
4c9ac07829 Updated Russia (Caucasus), Algeria, Egypt, India, Iceland, Morocco.
Signed-off-by: Viktor Govako <viktor.govako@gmail.com>
2025-02-18 06:59:42 +03:00
Viktor Govako
d6f1d1174c [generator] Do not generate traffic keys
Signed-off-by: Viktor Govako <viktor.govako@gmail.com>
2025-02-18 06:59:42 +03:00
Viktor Govako
1e31f4c8c0 [generator] Do not copy local planet.o5m dump, but make a symlink instead
Signed-off-by: Viktor Govako <viktor.govako@gmail.com>
Signed-off-by: Konstantin Pastbin <konstantin.pastbin@gmail.com>
2025-02-18 06:59:42 +03:00
vng
76584aaa52 [generator] Updated doc and removed obsolete
Signed-off-by: vng <viktor.govako@gmail.com>
2025-02-18 06:59:42 +03:00
5b5a27900a [strings] Regenerated
Signed-off-by: Ognjen Blagojevic <ognjen.d.blagojevic+om@gmail.com>
2025-02-18 06:43:13 +03:00
e44d8a19a3 [strings] Serbian translations, minor corrections
Signed-off-by: Ognjen Blagojevic <ognjen.d.blagojevic+om@gmail.com>
2025-02-18 06:43:13 +03:00
2290fc88c0 [android] Allow several lines in the PP subtitle
Signed-off-by: Konstantin Pastbin <konstantin.pastbin@gmail.com>
2025-02-18 06:05:00 +03:00
David Martinez
abdb58ba62 [debug] Add ?all-types debug command to show all internal types
Signed-off-by: David Martinez <47610359+dvdmrtnz@users.noreply.github.com>
2025-02-18 06:05:00 +03:00
8cc91e7372 [styles] Regenerated symbols
Signed-off-by: Harry Bond <me@hbond.xyz>
2025-02-17 18:20:26 +03:00
soshial
b831eba92d [android][l18n] fix localization of percentages in text
Signed-off-by: soshial <soshial@gmail.com>
2025-02-16 04:54:02 +03:00
29cf6abd7d [styles] Regenerate for new POIs
Signed-off-by: Konstantin Pastbin <konstantin.pastbin@gmail.com>
2025-02-15 16:08:46 +03:00
8b02809b5d [strings] Regenerate for new POIs
Signed-off-by: Konstantin Pastbin <konstantin.pastbin@gmail.com>
2025-02-15 16:08:46 +03:00
9a02f4e3fd [indexer] Add comms tower, cross and emergency=* to IsPoiChecker
Signed-off-by: Konstantin Pastbin <konstantin.pastbin@gmail.com>
2025-02-15 16:08:46 +03:00
d688c855a1 [classif] Add man_made=obelisk to historic=monument
Signed-off-by: Konstantin Pastbin <konstantin.pastbin@gmail.com>
2025-02-15 16:08:46 +03:00
38ed6221c2 [classif] Add man_made=flare
Signed-off-by: Konstantin Pastbin <konstantin.pastbin@gmail.com>
2025-02-15 16:08:46 +03:00
16cae17c50 [classif] Add emergency-mountain_rescue
Signed-off-by: Konstantin Pastbin <konstantin.pastbin@gmail.com>
2025-02-15 16:08:46 +03:00
944a61a52c [classif] Add emergency=lifeguard
Signed-off-by: Konstantin Pastbin <konstantin.pastbin@gmail.com>
2025-02-15 16:08:46 +03:00
8a3ff104a8 [styles] Fix hunting tower dark icon transparency
Signed-off-by: Konstantin Pastbin <konstantin.pastbin@gmail.com>
2025-02-15 16:08:46 +03:00
5e144a189a [classif] Add man_made=petroleum_well
Signed-off-by: Konstantin Pastbin <konstantin.pastbin@gmail.com>
2025-02-15 16:08:46 +03:00
8f8a4449a7 [classif] Add man_made=communications_tower
Signed-off-by: Konstantin Pastbin <konstantin.pastbin@gmail.com>
2025-02-15 16:08:46 +03:00
042e8ce025 [styles] Add a grey man_made=cross icon
Signed-off-by: Konstantin Pastbin <konstantin.pastbin@gmail.com>
2025-02-15 16:08:46 +03:00
72536b7c98 [classif] Add man_made=cross
Signed-off-by: Konstantin Pastbin <konstantin.pastbin@gmail.com>
2025-02-15 16:08:46 +03:00
4d40e3dfac [styles] Regenerates drules only
Signed-off-by: Hemang Manhas <hemangmanhas@gmail.com>
2025-02-14 17:04:15 +03:00
bb240c1aaf [strings] Regenerated
Signed-off-by: Hemang Manhas <hemangmanhas@gmail.com>
2025-02-14 17:04:15 +03:00
82dd6bb98a [styles] Adds shop=hearing_aids POI/map feature
- also added DeepL translations to types_strings.txt  & categories.txt
- Used audiologist icon with purple bg
- Changed priority to 500
- Changed zoom z17 to z18

Signed-off-by: Hemang Manhas <hemangmanhas@gmail.com>
2025-02-14 17:04:15 +03:00
Aditya Jagrani
1bc3490ca7 [ios] fixed manage route button in dark mode on iPad
Signed-off-by: Aditya Jagrani <adityajagrani8@gmail.com>
2025-02-14 17:08:42 +04:00
e1128b51fc [bookmarks] Make bookmark's title depend on map language
Resolves #10117

Signed-off-by: savsch <119003089+savsch@users.noreply.github.com>
2025-02-14 04:39:52 +03:00
1790a4dfca [ios] refactor ElevationProfileViewController
1. remove a stroryboard and implement VC and ElevationProfileDescriptionCell programmatically
2. move the description collection view over the chart
3. remove some unused code
4. add isChartViewInfoHidden to show/hide the info view and enable/disable user interation (will be used for the track recording)

Signed-off-by: Kiryl Kaveryn <kirylkaveryn@gmail.com>
2025-02-13 20:14:43 +04:00
5841d0f96e [icons] Fixes Opening hours Tint
Signed-off-by: Hemang Manhas <hemangmanhas@gmail.com>
2025-02-13 09:34:57 +03:00
2b746f0bf7 [Editor] Adds icons to editor (Fixes #6933)
Signed-off-by: Hemang Manhas <hemangmanhas@gmail.com>
2025-02-13 09:34:57 +03:00
0597afd475 [android] Remove dead scripts
Signed-off-by: Andrei Shkrob <github@shkrob.dev>
2025-02-13 09:28:18 +03:00
291daa91c0 [strings] Regenerated
Signed-off-by: Hemang Manhas <hemangmanhas@gmail.com>
2025-02-12 12:14:02 +03:00
bfa15962df [strings] Renames the string "world map" for clarity (Fixes:#9334)
Signed-off-by: Hemang Manhas <hemangmanhas@gmail.com>
2025-02-12 12:14:02 +03:00
cyber-toad
1f0a6760e0 [gpx] Add xml schema to color in gpx export
Signed-off-by: cyber-toad <the.cyber.toad@proton.me>
2025-02-11 23:16:32 +01:00
6b6b7d145e [ios] fix icloud initial sync bug when some files are not downloaded at start
https://github.com/organicmaps/organicmaps/issues/10221
Signed-off-by: Kiryl Kaveryn <kirylkaveryn@gmail.com>
2025-02-11 09:27:15 +00:00
9c8b6d934c [ios] start downloading icloud item with coordination
Attempt to fix the https://github.com/organicmaps/organicmaps/issues/10042

Signed-off-by: Kiryl Kaveryn <kirylkaveryn@gmail.com>
2025-02-11 09:25:51 +00:00
19701d83e6 [ios] fix Live activity widget issue when it recreates on every app opening
Description: https://github.com/organicmaps/organicmaps/issues/10222
Also this commit fixes the issue when the Live Activity is Disabled in the settings then the start recording fails

Signed-off-by: Kiryl Kaveryn <kirylkaveryn@gmail.com>
2025-02-11 08:37:59 +00:00
d8a594855a [ios] fix UIDatePicker style in the Editor
Signed-off-by: Kiryl Kaveryn <kirylkaveryn@gmail.com>
2025-02-11 08:36:42 +00:00
Osyotr
0a063dce44 [drape] Link OpenGL on windows
Signed-off-by: Osyotr <Osyotr@users.noreply.github.com>
2025-02-09 22:21:10 +01:00
Viktor Govako
d9de3eee6e [generator] Fixed possible bug in MaxspeedsMwmCollector.
Signed-off-by: Viktor Govako <viktor.govako@gmail.com>
2025-02-09 15:11:56 +03:00
Viktor Govako
87949cdb8e [tesselator] Temporary fix of possible error.
Signed-off-by: Viktor Govako <viktor.govako@gmail.com>
2025-02-09 15:11:56 +03:00
vng
3106cbd66d [generator] Fixed nodes in memory.
Signed-off-by: vng <viktor.govako@gmail.com>
2025-02-09 15:11:56 +03:00
vng
f3b5f72d93 [generator] Added RoadGeometry points stats.
Signed-off-by: vng <viktor.govako@gmail.com>
2025-02-09 15:11:56 +03:00
3c52bf59fd [styles] Regenerated for outdoors visibility changes
Signed-off-by: Konstantin Pastbin <konstantin.pastbin@gmail.com>
2025-02-07 19:15:30 +03:00
c349f39fe8 [styles][outdoor] Raise visibility of huts, hunting_stands, towers..
Signed-off-by: Konstantin Pastbin <konstantin.pastbin@gmail.com>
2025-02-07 19:15:30 +03:00
0f4b3eadd4 [tests][routing] Add a comment to the controversial UK_Canterbury_AvoidDismount bicycle test
Signed-off-by: Konstantin Pastbin <konstantin.pastbin@gmail.com>
2025-02-07 17:14:12 +03:00
a07a6d2d57 [tests][routing] Replace bicycle Madrid test
Signed-off-by: Konstantin Pastbin <konstantin.pastbin@gmail.com>
2025-02-07 17:14:12 +03:00
0edc581acf [routing] Update comments in bicycle weights and tests
Signed-off-by: Konstantin Pastbin <konstantin.pastbin@gmail.com>
2025-02-07 17:14:12 +03:00
199dd10c51 [tests][routing] Update bicycle speed tests
Signed-off-by: Konstantin Pastbin <konstantin.pastbin@gmail.com>
2025-02-07 17:14:12 +03:00
55aba70d2b [tests][routing] New cycling integration tests
Signed-off-by: Konstantin Pastbin <konstantin.pastbin@gmail.com>
2025-02-07 17:14:12 +03:00
23521ca309 [tests][routing] Update bicycle integration tests
Signed-off-by: Konstantin Pastbin <konstantin.pastbin@gmail.com>
2025-02-07 17:14:12 +03:00
d9a8fb1934 [routing] Tune cycling weights and ETAs
Signed-off-by: Konstantin Pastbin <konstantin.pastbin@gmail.com>
2025-02-07 17:14:12 +03:00
2d2a27dec7 [docs] install.md: omim_test to omim-test
omim_test is wrong, it's omim-test

Signed-off-by: Harry Bond <me@hbond.xyz>
2025-02-06 08:36:26 +03:00
8057e8c632 [styles] Fix unused color vars warnings
Signed-off-by: Konstantin Pastbin <konstantin.pastbin@gmail.com>
2025-02-06 06:48:55 +03:00
e89f80644f [android] Fixes Hyphenation word wrapping
Signed-off-by: Hemang Manhas <hemangmanhas@gmail.com>
2025-02-06 06:38:59 +03:00
6703faaae2 [android] Update NDK to r27c
Signed-off-by: Konstantin Pastbin <konstantin.pastbin@gmail.com>
2025-02-04 19:35:00 +03:00
49fce04798 [android] Update dependencies
Signed-off-by: Konstantin Pastbin <konstantin.pastbin@gmail.com>
2025-02-04 19:35:00 +03:00
7f45e7ffd7 [android] Update Gradle to 8.12.1
Signed-off-by: Konstantin Pastbin <konstantin.pastbin@gmail.com>
2025-02-04 19:35:00 +03:00
6c1b069de7 [android] Update AGP to 8.7.3
Signed-off-by: Konstantin Pastbin <konstantin.pastbin@gmail.com>
2025-02-04 19:35:00 +03:00
45dd46799e [ios] Use FASTLANE_ENABLE_BETA_DELIVER_SYNC_SCREENSHOTS
The new Fastlane feature enables incremental synchronization
of screenshots without fully removing them during each upload.

Signed-off-by: Roman Tsisyk <roman@tsisyk.com>
2025-02-04 09:15:00 +00:00
PRIZ ;]
98ba6f66a9
[Android] Allow reverse portrait orientation (#10176)
Signed-off-by: VoxelPrismatic <voxelprismatic@pm.me>
2025-02-04 05:14:48 +03:00
65f556e175 [android] Displays whole GPS error message in notification (Fixes:#9288)
Signed-off-by: Hemang Manhas <hemangmanhas@gmail.com>
2025-02-03 20:02:54 +00:00
945ae37209 [strings] Regenerated
Signed-off-by: Ognjen Blagojevic <ognjen.d.blagojevic+om@gmail.com>
2025-02-03 18:53:46 +03:00
c851828162 [strings] Serbian translations, minor corrections
Signed-off-by: Ognjen Blagojevic <ognjen.d.blagojevic+om@gmail.com>
2025-02-03 18:53:46 +03:00
84a49920cc [docs] improve translation instructions a bit
Signed-off-by: Harry Bond <me@hbond.xyz>
2025-02-01 19:01:32 +03:00
David Martinez
fb3f6e539c [strings] Regenerated
Signed-off-by: David Martinez <47610359+dvdmrtnz@users.noreply.github.com>
2025-02-01 18:51:19 +03:00
David Martinez
b3a56d8b6c Treat "only" as "yes" instead of "unknown" in YesNoUnknownFromString
Signed-off-by: David Martinez <47610359+dvdmrtnz@users.noreply.github.com>
2025-02-01 18:51:19 +03:00
David Martinez
431cf357a3 [editor][ios] Add outdoor_seating to editor
Signed-off-by: David Martinez <47610359+dvdmrtnz@users.noreply.github.com>
2025-02-01 18:51:19 +03:00
David Martinez
3493538c56 [editor][ios] Add self_service to editor
Signed-off-by: David Martinez <47610359+dvdmrtnz@users.noreply.github.com>
2025-02-01 18:51:19 +03:00
David Martinez
1664c549b4 [strings] Add self_service string to iOS
Signed-off-by: David Martinez <47610359+dvdmrtnz@users.noreply.github.com>
2025-02-01 18:51:19 +03:00
Colin Takushi
882cfd1a89
[core] Allow for rotation when scale is max. (#10137)
Signed-off-by: Colin Takushi <takushicolin@gmail.com>
2025-02-01 07:36:12 -03:00
457dac6f15 [android] Support using mouse wheel to zoom
Resolves #9841

Signed-off-by: savsch <119003089+savsch@users.noreply.github.com>
2025-02-01 11:13:32 +03:00
efcadd6f25 Refactor Editor storage to use diff based approach
Signed-off-by: map-per <map-per@gmx.de>
2025-01-31 17:46:04 -03:00
3ce6a9a29d [strings] Regenerate
Signed-off-by: Konstantin Pastbin <konstantin.pastbin@gmail.com>
2025-01-31 14:14:49 +03:00
598bb5f176 [strings] Bicycle charging translations updates
Signed-off-by: Konstantin Pastbin <konstantin.pastbin@gmail.com>
2025-01-31 14:14:49 +03:00
zell-mbc
4d7572e07c [categories] Added German translation for bike charging.
Signed-off-by: zell-mbc <thomas@zell-mbc.com>
2025-01-31 14:14:49 +03:00
519a851a66 [strings] Regenerated
Signed-off-by: map-per <map-per@gmx.de>
2025-01-30 22:12:16 +03:00
5c3a0c28cf [styles] Regenerated
Signed-off-by: map-per <map-per@gmx.de>
2025-01-30 22:12:16 +03:00
659af4bf1d Separate quarter and neighbourhood
Signed-off-by: map-per <map-per@gmx.de>
2025-01-30 22:12:16 +03:00
9b93d66f3d [ios] refactor the BottomMenuLayerButton
The runtime attributes were removed from the xib. Because it is really hard to debug them and there is no reason to set the bunch of the same attrs to the multiple items. Such configurations should be done pogrammatically.

Signed-off-by: Kiryl Kaveryn <kirylkaveryn@gmail.com>
2025-01-30 15:02:57 -03:00
6ed297c62e [ios] fix bug when the layers menu doesn't layouted properly on appear
On some ios versions the `tableView.layoutIfNeeded()` is called but the view doesnt set as it should be relayouted. This bug is founded on the iPhone Xs Max 18.2.
https://github.com/organicmaps/organicmaps/issues/10143#issuecomment-2620913027

Signed-off-by: Kiryl Kaveryn <kirylkaveryn@gmail.com>
2025-01-30 15:02:57 -03:00
db1b38b40f [ios] fix track recording bug first appearnce bug
Signed-off-by: Kiryl Kaveryn <kirylkaveryn@gmail.com>
2025-01-30 15:01:51 -03:00
f815ec1a17 [strings] Regenerated
Signed-off-by: map-per <map-per@gmx.de>
2025-01-30 19:57:01 +03:00
6dd2ea68d6 Improve place selection explanation
Signed-off-by: map-per <map-per@gmx.de>
2025-01-30 19:57:01 +03:00
DeshDeepakKant
d5635fd27b [bookmarks] Updated import bookmarks and tracks icon for consistency
Signed-off-by: Desh Deepak Kant <deshdeepakkant@gmail.com>
Signed-off-by: DeshDeepakKant <deshdeepakkant@gmail.com>
2025-01-30 19:53:02 +03:00
2f004d2d05 [strings] Regenerate
Signed-off-by: Konstantin Pastbin <konstantin.pastbin@gmail.com>
2025-01-30 19:46:59 +03:00
622e5d3a83 [strings] Remove unused AA string
Signed-off-by: Konstantin Pastbin <konstantin.pastbin@gmail.com>
2025-01-30 19:46:59 +03:00
73511395b0 [strings] Fix Dutch translation
Signed-off-by: Konstantin Pastbin <konstantin.pastbin@gmail.com>
2025-01-30 19:46:59 +03:00
Matheus Gomes
2ff9c4c690 Pt br translation (#4)
* Update Localizable.strings

Signed-off-by: Matheus Gomes <86851490+matheusgomesms@users.noreply.github.com>

* Update types_strings.txt

Signed-off-by: Matheus Gomes <86851490+matheusgomesms@users.noreply.github.com>

* Update strings.xml (#3)

Signed-off-by: Matheus Gomes <86851490+matheusgomesms@users.noreply.github.com>

---------

Signed-off-by: Matheus Gomes <86851490+matheusgomesms@users.noreply.github.com>
2025-01-30 19:46:51 +03:00
2b271761c1 [github] Remove explicit CODEOWNERS from translations
Signed-off-by: Konstantin Pastbin <konstantin.pastbin@gmail.com>
2025-01-30 19:35:03 +03:00
bd07332971 Merge branch 'release/2025.01.24' 2025-01-30 18:29:12 +03:00
KOIZUMI Satoru
9ff69ede4f [strings] fix typo in Japanese
Signed-off-by: KOIZUMI Satoru <koizumistr@minos.ocn.ne.jp>
2025-01-30 08:02:59 +03:00
7777a78448 [strings] Regenerated
Signed-off-by: map-per <map-per@gmx.de>
2025-01-29 16:41:26 +03:00
e02ae76196 Reorderd editor page fields
Signed-off-by: map-per <map-per@gmx.de>
2025-01-29 16:41:26 +03:00
2b82c234e0 Reordered place page details
Signed-off-by: map-per <map-per@gmx.de>
2025-01-29 13:55:25 +03:00
Andrew Shkrob
4b889f5b72 [android-auto] Make AA background service
Signed-off-by: Andrew Shkrob <andrew.shkrob.social@yandex.by>
2025-01-28 09:15:54 +03:00
Ferenc Géczi
0c9399e28c Add 2025.01.26 release notes to .metainfo.xml
Signed-off-by: Ferenc Géczi <ferenc.gm@gmail.com>
2025-01-27 13:38:54 +03:00
Andrew Shkrob
b8848eb39b [android-auto] Make AA background service
Signed-off-by: Andrew Shkrob <andrew.shkrob.social@yandex.by>
2025-01-26 22:06:37 +03:00
7865ecb18b Don't hide login for offline users
Signed-off-by: map-per <map-per@gmx.de>
2025-01-26 21:54:01 +03:00
870 changed files with 160996 additions and 276056 deletions

101
.github/CODEOWNERS vendored
View file

@ -1,66 +1,69 @@
# All non-assigned.
* @organicmaps/mergers
# Visual design.
android/app/src/main/res/drawable*/ @organicmaps/design
android/app/src/main/res/font/ @organicmaps/design
android/app/src/main/res/mipmap*/ @organicmaps/design
data/*.ttf @organicmaps/design
data/resources*/ @organicmaps/design
data/search-icons/ @organicmaps/design
data/styles/default/light/**/*.png @organicmaps/design
data/styles/default/light/**/*.svg @organicmaps/design
data/styles/default/dark/**/*.png @organicmaps/design
data/styles/default/dark/**/*.svg @organicmaps/design
iphone/Maps/Images.xcassets/ @organicmaps/design
/android/app/src/main/res/drawable*/ @organicmaps/design
/android/app/src/main/res/font/ @organicmaps/design
/android/app/src/main/res/mipmap*/ @organicmaps/design
/data/*.ttf @organicmaps/design
/data/resources-svg/ @organicmaps/design
/data/search-icons/ @organicmaps/design
/iphone/Maps/Images.xcassets/ @organicmaps/design
# Android.
android/ @organicmaps/android
android/app/src/main/java/app/organicmaps/car/ @organicmaps/android-auto
docs/ANDROID_LOCATION_TEST.md @organicmaps/android
docs/JAVA_STYLE.md @organicmaps/android
/android/ @organicmaps/android
/android/app/src/main/java/app/organicmaps/car/ @organicmaps/android-auto
/docs/ANDROID_LOCATION_TEST.md @organicmaps/android
/docs/JAVA_STYLE.md @organicmaps/android
# no owner for translation changes
/android/app/src/main/res/values*/strings.xml
# iOS.
iphone/ @organicmaps/ios
xcode/ @organicmaps/ios
docs/OBJC_STYLE.md @organicmaps/ios
/iphone/ @organicmaps/ios
/xcode/ @organicmaps/ios
/docs/OBJC_STYLE.md @organicmaps/ios
# no owner for translation changes
/iphone/plist.txt
/iphone/Maps/LocalizedStrings/
# Qt
qt/ @organicmaps/qt
/qt/ @organicmaps/qt
# Rendering
drape/ @organicmaps/rendering
drape_frontend/ @organicmaps/rendering
/drape/ @organicmaps/rendering
/drape_frontend/ @organicmaps/rendering
# Map Data.
tools/python/maps_generator/ @organicmaps/data
generator/ @organicmaps/data
topography_generator/ @organicmaps/data
data/borders/ @organicmaps/data
data/conf/isolines/ @organicmaps/data
docs/SUBWAY_GENERATION.md @organicmaps/data
docs/MAPS.md @organicmaps/data
docs/EXPERIMENTAL_PUBLIC_TRANSPORT_SUPPORT.md @organicmaps/data
/tools/python/maps_generator/ @organicmaps/data
/generator/ @organicmaps/data
/topography_generator/ @organicmaps/data
/data/borders/ @organicmaps/data
/data/conf/isolines/ @organicmaps/data
/docs/SUBWAY_GENERATION.md @organicmaps/data
/docs/MAPS.md @organicmaps/data
/docs/EXPERIMENTAL_PUBLIC_TRANSPORT_SUPPORT.md @organicmaps/data
# no owner (changed often to add a new POI)
/generator/generator_tests/osm_type_test.cpp
# Map Styles.
data/styles/ @organicmaps/styles
data/types.txt @organicmaps/styles
data/visibility.txt @organicmaps/styles
data/mapcss-mapping.csv @organicmaps/styles
data/replaced_tags.txt @organicmaps/styles
data/classificator.txt @organicmaps/styles
data/drules_* @organicmaps/styles
docs/STYLES.md
tools/kothic/ @organicmaps/styles
/data/styles/ @organicmaps/styles
/data/types.txt @organicmaps/styles
/data/visibility.txt @organicmaps/styles
/data/mapcss-mapping.csv @organicmaps/styles
/data/replaced_tags.txt @organicmaps/styles
/data/classificator.txt @organicmaps/styles
/data/drules_* @organicmaps/styles
/docs/STYLES.md
/tools/kothic/ @organicmaps/styles
# DevOps.
.github/workflows @organicmaps/devops
android/*gradle* @organicmaps/devops
docs/RELEASE_MANAGEMENT.md @organicmaps/devops
xcode/fastlane/ @organicmaps/devops
/.github/workflows @organicmaps/devops
/android/*gradle* @organicmaps/devops
/docs/RELEASE_MANAGEMENT.md @organicmaps/devops
/xcode/fastlane/ @organicmaps/devops
# Growth.
README.md @organicmaps/growth
.github/FUNDING.yml @organicmaps/growth
android/app/src/fdroid/play/ @organicmaps/growth
android/app/src/google/play/ @organicmaps/growth
iphone/metadata/ @organicmaps/growth
/.github/FUNDING.yml @organicmaps/growth
/android/app/src/fdroid/play/ @organicmaps/growth
/android/app/src/google/play/ @organicmaps/growth
/iphone/metadata/ @organicmaps/growth
# Legal.
LEGAL @organicmaps/legal
LICENSE @organicmaps/legal
NOTICE @organicmaps/legal
CONTRIBUTORS @organicmaps/legal
docs/CODE_OF_CONDUCT.md @organicmaps/legal
docs/DCO.md @organicmaps/legal
docs/GOVERNANCE.md @organicmaps/legal
/docs/CODE_OF_CONDUCT.md @organicmaps/legal
/docs/DCO.md @organicmaps/legal
/docs/GOVERNANCE.md @organicmaps/legal

View file

@ -61,6 +61,10 @@ jobs:
libgl1-mesa-dev \
libglvnd-dev \
libharfbuzz-dev \
libxrandr-dev \
libxinerama-dev \
libxcursor-dev \
libxi-dev \
qt6-base-dev \
libqt6svg6-dev \
qt6-positioning-dev \
@ -128,6 +132,10 @@ jobs:
libgl1-mesa-dev \
libglvnd-dev \
libharfbuzz-dev \
libxrandr-dev \
libxinerama-dev \
libxcursor-dev \
libxi-dev \
qt6-base-dev \
libqt6svg6-dev \
qt6-positioning-dev \

22
.github/workflows/stale.yml vendored Normal file
View file

@ -0,0 +1,22 @@
name: Close stale PRs
on:
schedule:
- cron: "0 0 * * *" # Runs every day at midnight
jobs:
stale:
runs-on: ubuntu-latest
permissions:
pull-requests: write
steps:
- uses: actions/stale@v9
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-pr-stale: 180 # 6 months before warning
days-before-pr-close: 365 # Closed after 12 months
stale-pr-label: "stale"
stale-pr-message: "Hi! This PR has been inactive for 6 months. If it's still relevant, please update it to let us know youd like to keep it open 😊"
close-pr-message: "This PR has been automatically closed after 12 months of inactivity."
days-before-issue-stale: -1 # Issues are never stale
days-before-issue-close: -1 # Issues are never closed
remove-stale-when-updated: true

View file

@ -1,31 +0,0 @@
name: Validate translation strings
on:
workflow_dispatch: # Manual trigger
pull_request:
paths:
- .github/workflows/strings-check.yaml # Run check on self change
- data/strings/strings.txt
- data/strings/types_strings.txt
- data/strings/sound.txt
- data/countries_names.txt
- iphone/plist.txt
- tools/python/strings_utils.py
jobs:
validate-translation-strings:
name: Validate translation strings
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3'
- name: Validate string files
shell: bash
run: |
for f in data/strings/strings.txt data/strings/types_strings.txt data/strings/sound.txt data/countries_names.txt iphone/plist.txt; do
./tools/python/strings_utils.py --validate $f -o
done;
git diff --exit-code

1
.gitignore vendored
View file

@ -20,6 +20,7 @@ data/drules_proto_default_design.bin
data/colors_design.txt
data/patterns_design.txt
data/bookmarks
data/edits.xml
# Compiled Python
*.pyc

12
.gitmodules vendored
View file

@ -7,9 +7,6 @@
[submodule "3party/protobuf/protobuf"]
path = 3party/protobuf/protobuf
url = https://github.com/organicmaps/protobuf.git
[submodule "tools/twine"]
path = tools/twine
url = https://github.com/organicmaps/twine.git
[submodule "3party/Vulkan-Headers"]
path = 3party/Vulkan-Headers
url = https://github.com/KhronosGroup/Vulkan-Headers.git
@ -59,3 +56,12 @@
[submodule "3party/utfcpp"]
path = 3party/utfcpp
url = https://github.com/nemtrif/utfcpp.git
[submodule "3party/glfw"]
path = 3party/glfw
url = https://github.com/glfw/glfw.git
[submodule "3party/CMake-MetalShaderSupport"]
path = 3party/CMake-MetalShaderSupport
url = https://github.com/dpogue/CMake-MetalShaderSupport.git
[submodule "3party/imgui/imgui"]
path = 3party/imgui/imgui
url = https://github.com/ocornut/imgui

@ -0,0 +1 @@
Subproject commit 989857d2e5e54869c35ad06fb21a67d12a2dbc67

View file

@ -66,4 +66,19 @@ add_subdirectory(vulkan_wrapper)
if (PLATFORM_DESKTOP)
add_subdirectory(libtess2)
set(GLFW_BUILD_DOCS OFF CACHE BOOL "")
set(GLFW_BUILD_EXAMPLES OFF CACHE BOOL "")
set(GLFW_BUILD_TESTS OFF CACHE BOOL "")
set(GLFW_INSTALL OFF CACHE BOOL "")
set(GLFW_VULKAN_STATIC OFF CACHE BOOL "")
set(GLFW_BUILD_WAYLAND OFF CACHE BOOL "")
# Disable ARC for glfw and re-enable after it because it's globally set in the root CMakeLists.txt
set(CMAKE_OBJC_FLAGS "")
add_subdirectory(glfw)
set_target_properties(glfw PROPERTIES UNITY_BUILD OFF)
set_target_properties(glfw PROPERTIES XCODE_ATTRIBUTE_CLANG_ENABLE_OBJC_ARC NO)
set(CMAKE_OBJC_FLAGS -fobjc-arc)
add_subdirectory(imgui)
endif()

1
3party/glfw Submodule

@ -0,0 +1 @@
Subproject commit 21fea01161e0d6b70c0c5c1f52dc8e7a7df14a50

View file

@ -0,0 +1,16 @@
project(imgui)
set(SRC
imgui/imgui_draw.cpp
imgui/imgui_tables.cpp
imgui/imgui_widgets.cpp
imgui/imgui.cpp
imgui/backends/imgui_impl_glfw.cpp
)
add_library(${PROJECT_NAME} ${SRC})
target_include_directories(${PROJECT_NAME}
PRIVATE ${OMIM_ROOT}/3party/glfw/include
PUBLIC ${OMIM_ROOT}/3party/imgui/imgui
PUBLIC .
)

1
3party/imgui/imgui Submodule

@ -0,0 +1 @@
Subproject commit 6982ce43f5b143c5dce5fab0ce07dd4867b705ae

View file

@ -90,6 +90,19 @@ else()
message(FATAL_ERROR "Unsupported platform: ${CMAKE_SYSTEM_NAME}")
endif()
if(${PLATFORM_MAC})
set(XCODE_ATTRIBUTE_CLANG_ENABLE_OBJC_ARC YES)
# Metal language support
list(APPEND CMAKE_MODULE_PATH ${OMIM_ROOT}/3party/CMake-MetalShaderSupport/cmake)
include(CheckLanguage)
include(MetalShaderSupport)
check_language(Metal)
if(CMAKE_Metal_COMPILER)
enable_language(Metal)
endif()
endif()
# Sanitizer
if (PLATFORM_DESKTOP)
# https://clang.llvm.org/docs/UsersManual.html#controlling-code-generation
@ -265,16 +278,6 @@ find_package(Threads REQUIRED)
# Scripts
if (NOT CMAKE_HOST_WIN32)
execute_process(
COMMAND "${OMIM_ROOT}/tools/unix/check_cert.sh"
RESULT_VARIABLE CheckCertResult
)
if (CheckCertResult)
message(FATAL_ERROR "Certificate check failed")
endif()
endif()
if (NOT PLATFORM_IPHONE AND NOT PLATFORM_ANDROID)
list(APPEND qt_components Core Network)
if (NOT SKIP_QT_GUI OR NOT SKIP_TESTS OR PYBINDINGS)
@ -394,6 +397,7 @@ if (PLATFORM_DESKTOP)
add_subdirectory(qt)
omim_add_tool_subdirectory(skin_generator)
endif()
add_subdirectory(dev_sandbox)
endif()
omim_add_test_subdirectory(qt_tstfrm)

View file

@ -1,5 +1,5 @@
<div align="center">
<img src="/qt/res/logo.png" height="100"/>
<img src="qt/res/logo.png" height="100"/>
</div>
<h1 align="center"">Organic Maps</h1>

View file

@ -22,7 +22,7 @@ buildscript {
googleFirebaseServicesDefault
dependencies {
classpath 'com.android.tools.build:gradle:8.7.2'
classpath 'com.android.tools.build:gradle:8.7.3'
if (googleFirebaseServicesEnabled) {
println('Building with Google Firebase Services')
@ -100,7 +100,7 @@ android {
// All properties are read from gradle.properties file
compileSdk propCompileSdkVersion.toInteger()
ndkVersion '27.1.12297006'
ndkVersion '27.2.12479018'
defaultConfig {
// Default package name is taken from the manifest and should be app.organicmaps
@ -363,7 +363,7 @@ android {
}
dependencies {
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.3'
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.4'
// Google Play Location Services
//
@ -379,7 +379,7 @@ dependencies {
huaweiImplementation 'com.google.android.gms:play-services-location:21.3.0'
// This is the microG project's re-implementation which is permissible on
// F-droid because it's Apache-2.0.
fdroidImplementation 'org.microg.gms:play-services-location:0.3.4.240913'
fdroidImplementation 'org.microg.gms:play-services-location:0.3.6.244735'
// Google Firebase Services
if (googleFirebaseServicesEnabled) {
@ -396,11 +396,11 @@ dependencies {
// We don't use Kotlin, but some dependencies are actively using it.
// See https://stackoverflow.com/a/75719642
implementation 'androidx.core:core:1.15.0'
implementation(platform('org.jetbrains.kotlin:kotlin-bom:2.0.21'))
implementation(platform('org.jetbrains.kotlin:kotlin-bom:2.1.10'))
implementation 'androidx.annotation:annotation:1.9.1'
implementation 'androidx.appcompat:appcompat:1.7.0'
implementation 'androidx.car.app:app:1.7.0-beta03'
implementation 'androidx.car.app:app-projected:1.7.0-beta03'
implementation 'androidx.car.app:app:1.7.0-rc01'
implementation 'androidx.car.app:app-projected:1.7.0-rc01'
implementation 'androidx.constraintlayout:constraintlayout:2.2.0'
implementation 'androidx.fragment:fragment:1.8.5'
implementation 'androidx.preference:preference:1.2.1'
@ -417,7 +417,7 @@ dependencies {
// Test Dependencies
androidTestImplementation 'androidx.test.ext:junit:1.2.1'
testImplementation 'junit:junit:4.13.2'
testImplementation 'org.mockito:mockito-core:5.12.0'
testImplementation 'org.mockito:mockito-core:5.15.2'
testImplementation 'org.mockito:mockito-inline:5.2.0'
}

View file

@ -37,6 +37,7 @@
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE"/>
<!--
Android 13 (API level 33) and higher supports a runtime permission for sending non-exempt (including Foreground
@ -86,6 +87,7 @@
<activity
android:name="app.organicmaps.SplashActivity"
android:configChanges="orientation|screenSize|smallestScreenSize|density|screenLayout|uiMode|keyboard|keyboardHidden|navigation"
android:screenOrientation="fullUser"
android:exported="true">
<intent-filter>
@ -120,7 +122,7 @@
<data android:scheme="https"/>
<data android:host="omaps.app"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
@ -344,7 +346,8 @@
<activity
android:name="app.organicmaps.DownloadResourcesLegacyActivity"
android:configChanges="orientation|screenLayout|screenSize"/>
android:configChanges="orientation|screenLayout|screenSize"
android:screenOrientation="fullUser"/>
<activity-alias
android:name="app.organicmaps.DownloadResourcesActivity"
@ -361,6 +364,7 @@
android:name="app.organicmaps.MwmActivity"
android:launchMode="singleTask"
android:configChanges="uiMode"
android:screenOrientation="fullUser"
android:windowSoftInputMode="stateAlwaysHidden|adjustPan"/>
<activity-alias
@ -371,6 +375,7 @@
<activity
android:name="app.organicmaps.downloader.DownloaderActivity"
android:configChanges="orientation|screenLayout|screenSize"
android:screenOrientation="fullUser"
android:label="@string/download_maps"
android:parentActivityName="app.organicmaps.MwmActivity"
android:windowSoftInputMode="adjustResize" />
@ -378,6 +383,7 @@
<activity
android:name="app.organicmaps.search.SearchActivity"
android:configChanges="orientation|screenLayout|screenSize"
android:screenOrientation="fullUser"
android:label="@string/search_map"
android:parentActivityName="app.organicmaps.MwmActivity"
android:windowSoftInputMode="stateVisible|adjustResize" />
@ -385,6 +391,7 @@
<activity
android:name="app.organicmaps.settings.SettingsActivity"
android:configChanges="orientation|screenLayout|screenSize"
android:screenOrientation="fullUser"
android:label="@string/settings"
android:parentActivityName="app.organicmaps.MwmActivity" />
@ -402,6 +409,7 @@
<activity
android:name="app.organicmaps.bookmarks.BookmarkCategoriesActivity"
android:configChanges="orientation|screenLayout|screenSize"
android:screenOrientation="fullUser"
android:label="@string/bookmarks_and_tracks"
android:parentActivityName="app.organicmaps.MwmActivity"
android:windowSoftInputMode="adjustResize" />
@ -409,6 +417,7 @@
<activity
android:name="app.organicmaps.bookmarks.BookmarkListActivity"
android:configChanges="orientation|screenLayout|screenSize"
android:screenOrientation="fullUser"
android:label="@string/bookmarks"
android:parentActivityName="app.organicmaps.bookmarks.BookmarkCategoriesActivity"
android:windowSoftInputMode="adjustResize" />
@ -416,6 +425,7 @@
<activity
android:name="app.organicmaps.editor.EditorActivity"
android:configChanges="orientation|screenLayout|screenSize"
android:screenOrientation="fullUser"
android:label="@string/edit_place"
android:parentActivityName="app.organicmaps.MwmActivity"
android:windowSoftInputMode="adjustResize" />
@ -454,14 +464,16 @@
<service
android:name="app.organicmaps.car.CarAppService"
android:exported="true"
android:foregroundServiceType="specialUse"
tools:ignore="ExportedService">
<intent-filter>
<action android:name="androidx.car.app.CarAppService" />
<category android:name="androidx.car.app.category.NAVIGATION" />
</intent-filter>
<property android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
android:value="@string/android_auto_fgs_explanation_for_special_use"/>
<intent-filter>
<action android:name="androidx.car.app.action.NAVIGATE" />
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="geo" />
</intent-filter>
</service>
<service
@ -478,6 +490,13 @@
android:stopWithTask="false"
/>
<service
android:name=".downloader.DownloaderService"
android:foregroundServiceType="dataSync"
android:exported="false"
android:enabled="true"
android:stopWithTask="false"/>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${FILE_PROVIDER_PLACEHOLDER}"
@ -493,8 +512,8 @@
<meta-data android:name="android.webkit.WebView.EnableSafeBrowsing" android:value="false" />
<!-- Disable Google's anonymous stats collection -->
<meta-data android:name="android.webkit.WebView.MetricsOptOut" android:value="true" />
<!-- Version >= 3.0. Dex Dual Mode support for compatible Samsung devices.
<!-- Version >= 3.0. Dex Dual Mode support for compatible Samsung devices.
See the documentation: https://developer.samsung.com/samsung-dex/modify-optimizing.html //-->
<meta-data android:name="com.samsung.android.multidisplay.keep_process_alive" android:value="true" />

View file

@ -22,11 +22,13 @@ set(SRC
app/organicmaps/vulkan/android_vulkan_context_factory.hpp
# JNI sources
app/organicmaps/sdk/search/DisplayedCategories.cpp
app/organicmaps/sdk/search/SearchEngine.cpp
app/organicmaps/sdk/search/SearchRecents.cpp
app/organicmaps/core/jni_helper.cpp
app/organicmaps/core/jni_java_methods.cpp
app/organicmaps/core/logging.cpp
app/organicmaps/bookmarks/data/BookmarkManager.cpp
app/organicmaps/DisplayedCategories.cpp
app/organicmaps/DownloadResourcesLegacyActivity.cpp
app/organicmaps/editor/Editor.cpp
app/organicmaps/editor/OpeningHours.cpp
@ -38,8 +40,6 @@ set(SRC
app/organicmaps/MapManager.cpp
app/organicmaps/MwmApplication.cpp
app/organicmaps/routing/RoutingOptions.cpp
app/organicmaps/SearchEngine.cpp
app/organicmaps/SearchRecents.cpp
app/organicmaps/settings/UnitLocale.cpp
app/organicmaps/settings/MapLanguageCode.cpp
app/organicmaps/sound/tts.cpp

View file

@ -1578,6 +1578,12 @@ Java_app_organicmaps_Framework_nativeAddRoutePoint(JNIEnv * env, jclass, jstring
frm()->GetRoutingManager().AddRoutePoint(std::move(data));
}
JNIEXPORT void JNICALL
Java_app_organicmaps_Framework_nativeRemoveRoutePoints(JNIEnv * env, jclass)
{
frm()->GetRoutingManager().RemoveRoutePoints();
}
JNIEXPORT void JNICALL
Java_app_organicmaps_Framework_nativeRemoveRoutePoint(JNIEnv * env, jclass,
jint markType, jint intermediateIndex)
@ -1627,6 +1633,13 @@ Java_app_organicmaps_Framework_nativeGetRoutePoints(JNIEnv * env, jclass)
});
}
JNIEXPORT void JNICALL
Java_app_organicmaps_Framework_nativeMoveRoutePoint(JNIEnv * env, jclass,
jint currentIndex, jint targetIndex)
{
frm()->GetRoutingManager().MoveRoutePoint(currentIndex, targetIndex);
}
JNIEXPORT jobject JNICALL
Java_app_organicmaps_Framework_nativeGetTransitRouteInfo(JNIEnv * env, jclass)
{

View file

@ -1,40 +0,0 @@
#include "Framework.hpp"
#include "search/result.hpp"
#include "app/organicmaps/core/jni_helper.hpp"
#include "app/organicmaps/core/jni_java_methods.hpp"
using SearchRequest = search::QuerySaver::SearchRequest;
extern "C"
{
JNIEXPORT void JNICALL
Java_app_organicmaps_search_SearchRecents_nativeGetList(JNIEnv * env, jclass thiz, jobject result)
{
auto const & items = g_framework->NativeFramework()->GetSearchAPI().GetLastSearchQueries();
if (items.empty())
return;
auto const listAddMethod = jni::ListBuilder::Instance(env).m_add;
for (SearchRequest const & item : items)
{
jni::TScopedLocalRef str(env, jni::ToJavaString(env, item.second));
env->CallBooleanMethod(result, listAddMethod, str.get());
}
}
JNIEXPORT void JNICALL
Java_app_organicmaps_search_SearchRecents_nativeAdd(JNIEnv * env, jclass thiz, jstring locale, jstring query)
{
SearchRequest const sr(jni::ToNativeString(env, locale), jni::ToNativeString(env, query));
g_framework->NativeFramework()->GetSearchAPI().SaveSearchQuery(sr);
}
JNIEXPORT void JNICALL
Java_app_organicmaps_search_SearchRecents_nativeClear(JNIEnv * env, jclass thiz)
{
g_framework->NativeFramework()->GetSearchAPI().ClearSearchHistory();
}
}

View file

@ -28,7 +28,7 @@ void InjectMetadata(JNIEnv * env, jclass const clazz, jobject const mapObject, o
//jobject CreatePopularity(JNIEnv * env, place_page::Info const & info)
//{
// static jclass const popularityClass =
// jni::GetGlobalClassRef(env, "app/organicmaps/search/Popularity");
// jni::GetGlobalClassRef(env, "app/organicmaps/sdk/search/Popularity");
// static jmethodID const popularityConstructor =
// jni::GetConstructorID(env, popularityClass, "(I)V");
// auto const popularityValue = info.GetPopularity();
@ -57,7 +57,7 @@ jobject CreateMapObject(JNIEnv * env, place_page::Info const & info, int mapObje
"Ljava/lang/String;" // appId
"Lapp/organicmaps/routing/RoutePointInfo;" // routePointInfo
"I" // openingMode
"Lapp/organicmaps/search/Popularity;" // popularity
"Lapp/organicmaps/sdk/search/Popularity;" // popularity
"Ljava/lang/String;" // description
"I" // roadWarnType
"[Ljava/lang/String;" // rawTypes
@ -105,7 +105,7 @@ jobject CreateBookmark(JNIEnv *env, const place_page::Info &info,
"(Lapp/organicmaps/bookmarks/data/FeatureId;JJLjava/lang/String;"
"Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;"
"Lapp/organicmaps/routing/RoutePointInfo;"
"ILapp/organicmaps/search/Popularity;Ljava/lang/String;"
"ILapp/organicmaps/sdk/search/Popularity;Ljava/lang/String;"
"[Ljava/lang/String;)V");
static jmethodID const featureCtorId =
jni::GetConstructorID(env, g_featureIdClazz, "(Ljava/lang/String;JI)V");
@ -165,12 +165,13 @@ jobject CreateElevationInfo(JNIEnv * env, ElevationInfo const & info)
"[Lapp/organicmaps/bookmarks/data/ElevationInfo$Point;"
"IIIIIJ)V");
jni::TScopedLocalObjectArrayRef jPoints(env, ToElevationPointArray(env, info.GetPoints()));
// TODO (KK): elevation info should have only the elevation data - see the https://github.com/organicmaps/organicmaps/pull/10063
return env->NewObject(g_elevationInfoClazz, ctorId,
jPoints.get(),
static_cast<jint>(info.GetAscent()),
static_cast<jint>(info.GetDescent()),
static_cast<jint>(info.GetMinAltitude()),
static_cast<jint>(info.GetMaxAltitude()),
// static_cast<jint>(info.GetAscent()),
// static_cast<jint>(info.GetDescent()),
// static_cast<jint>(info.GetMinAltitude()),
// static_cast<jint>(info.GetMaxAltitude()),
static_cast<jint>(info.GetDifficulty()));
}

View file

@ -591,6 +591,15 @@ Java_app_organicmaps_bookmarks_data_BookmarkManager_nativeSetAllCategoriesVisibi
frm()->GetBookmarkManager().SetAllCategoriesVisibility(static_cast<bool>(visible));
}
JNIEXPORT void JNICALL
Java_app_organicmaps_bookmarks_data_BookmarkManager_nativePrepareTrackFileForSharing(JNIEnv * env, jclass, jlong trackId, jint kmlFileType)
{
frm()->GetBookmarkManager().PrepareTrackFileForSharing(static_cast<kml::TrackId>(trackId), [env](BookmarkManager::SharingResult const & result)
{
OnPreparedFileForSharing(env, result);
}, static_cast<KmlFileType>(kmlFileType));
}
JNIEXPORT void JNICALL
Java_app_organicmaps_bookmarks_data_BookmarkManager_nativePrepareFileForSharing(JNIEnv * env, jclass, jlongArray catIds, jint kmlFileType)
{

View file

@ -149,7 +149,8 @@ Java_app_organicmaps_editor_Editor_nativeHasWifi(JNIEnv *, jclass)
JNIEXPORT void JNICALL
Java_app_organicmaps_editor_Editor_nativeSetHasWifi(JNIEnv *, jclass, jboolean hasWifi)
{
g_editableMapObject.SetInternet(hasWifi ? feature::Internet::Wlan : feature::Internet::Unknown);
if (hasWifi != (g_editableMapObject.GetInternet() == feature::Internet::Wlan))
g_editableMapObject.SetInternet(hasWifi ? feature::Internet::Wlan : feature::Internet::Unknown);
}
JNIEXPORT jboolean JNICALL
@ -362,7 +363,11 @@ Java_app_organicmaps_editor_Editor_nativeStartEdit(JNIEnv *, jclass)
{
::Framework * frm = g_framework->NativeFramework();
if (!frm->HasPlacePageInfo())
{
ASSERT(g_editableMapObject.GetEditingLifecycle() == osm::EditingLifecycle::CREATED,
("PlacePageInfo should only be empty for new features."));
return;
}
place_page::Info const & info = g_framework->GetPlacePageInfo();
CHECK(frm->GetEditableMapObject(info.GetID(), g_editableMapObject), ("Invalid feature in the place page."));

View file

@ -5,8 +5,7 @@
extern "C"
{
JNIEXPORT jobjectArray JNICALL
Java_app_organicmaps_search_DisplayedCategories_nativeGetKeys(JNIEnv * env, jclass clazz)
JNIEXPORT jobjectArray JNICALL Java_app_organicmaps_sdk_search_DisplayedCategories_nativeGetKeys(JNIEnv * env, jclass)
{
::Framework * fr = g_framework->NativeFramework();
ASSERT(fr, ());

View file

@ -39,7 +39,7 @@ Results g_results;
// Timestamp of last search query. Results with older stamps are ignored.
jlong g_queryTimestamp;
// Implements 'NativeSearchListener' java interface.
// Implements 'SearchListener' java interface.
jobject g_javaListener;
jmethodID g_updateResultsId;
jmethodID g_endResultsId;
@ -52,7 +52,7 @@ jmethodID g_descriptionConstructor;
jclass g_popularityClass;
jmethodID g_popularityConstructor;
// Implements 'NativeMapSearchListener' java interface.
// Implements 'MapSearchListener' java interface.
jmethodID g_mapResultsMethod;
jclass g_mapResultClass;
jmethodID g_mapResultCtor;
@ -232,21 +232,21 @@ void OnBookmarksSearchResults(search::BookmarksSearchParams::Results results,
extern "C"
{
JNIEXPORT void JNICALL
Java_app_organicmaps_search_SearchEngine_nativeInit(JNIEnv * env, jobject thiz)
Java_app_organicmaps_sdk_search_SearchEngine_nativeInit(JNIEnv * env, jobject thiz)
{
g_javaListener = env->NewGlobalRef(thiz);
// public void onResultsUpdate(@NonNull SearchResult[] results, long timestamp)
g_updateResultsId = jni::GetMethodID(env, g_javaListener, "onResultsUpdate",
"([Lapp/organicmaps/search/SearchResult;J)V");
"([Lapp/organicmaps/sdk/search/SearchResult;J)V");
// public void onResultsEnd(long timestamp)
g_endResultsId = jni::GetMethodID(env, g_javaListener, "onResultsEnd", "(J)V");
g_resultClass = jni::GetGlobalClassRef(env, "app/organicmaps/search/SearchResult");
g_resultClass = jni::GetGlobalClassRef(env, "app/organicmaps/sdk/search/SearchResult");
g_resultConstructor = jni::GetConstructorID(
env, g_resultClass,
"(Ljava/lang/String;Lapp/organicmaps/search/SearchResult$Description;DD[I[I"
"Lapp/organicmaps/search/Popularity;)V");
"(Ljava/lang/String;Lapp/organicmaps/sdk/search/SearchResult$Description;DD[I[I"
"Lapp/organicmaps/sdk/search/Popularity;)V");
g_suggestConstructor = jni::GetConstructorID(env, g_resultClass, "(Ljava/lang/String;Ljava/lang/String;DD[I[I)V");
g_descriptionClass = jni::GetGlobalClassRef(env, "app/organicmaps/search/SearchResult$Description");
g_descriptionClass = jni::GetGlobalClassRef(env, "app/organicmaps/sdk/search/SearchResult$Description");
/*
Description(FeatureId featureId, String featureType, String region, Distance distance,
String description, int openNow, int minutesUntilOpen, int minutesUntilClosed,
@ -257,12 +257,12 @@ extern "C"
"Ljava/lang/String;Ljava/lang/String;Lapp/organicmaps/util/Distance;"
"Ljava/lang/String;IIIZ)V");
g_popularityClass = jni::GetGlobalClassRef(env, "app/organicmaps/search/Popularity");
g_popularityClass = jni::GetGlobalClassRef(env, "app/organicmaps/sdk/search/Popularity");
g_popularityConstructor = jni::GetConstructorID(env, g_popularityClass, "(I)V");
g_mapResultsMethod = jni::GetMethodID(env, g_javaListener, "onMapSearchResults",
"([Lapp/organicmaps/search/NativeMapSearchListener$Result;JZ)V");
g_mapResultClass = jni::GetGlobalClassRef(env, "app/organicmaps/search/NativeMapSearchListener$Result");
"([Lapp/organicmaps/sdk/search/MapSearchListener$Result;JZ)V");
g_mapResultClass = jni::GetGlobalClassRef(env, "app/organicmaps/sdk/search/MapSearchListener$Result");
g_mapResultCtor = jni::GetConstructorID(env, g_mapResultClass, "(Ljava/lang/String;Ljava/lang/String;)V");
g_updateBookmarksResultsId =
@ -271,7 +271,7 @@ extern "C"
jni::GetMethodID(env, g_javaListener, "onBookmarkSearchResultsEnd", "([JJ)V");
}
JNIEXPORT jboolean JNICALL Java_app_organicmaps_search_SearchEngine_nativeRunSearch(
JNIEXPORT jboolean JNICALL Java_app_organicmaps_sdk_search_SearchEngine_nativeRunSearch(
JNIEnv * env, jclass clazz, jbyteArray bytes, jboolean isCategory,
jstring lang, jlong timestamp, jboolean hasPosition, jdouble lat, jdouble lon)
{
@ -288,7 +288,7 @@ extern "C"
return searchStarted;
}
JNIEXPORT void JNICALL Java_app_organicmaps_search_SearchEngine_nativeRunInteractiveSearch(
JNIEXPORT void JNICALL Java_app_organicmaps_sdk_search_SearchEngine_nativeRunInteractiveSearch(
JNIEnv * env, jclass clazz, jbyteArray bytes, jboolean isCategory,
jstring lang, jlong timestamp, jboolean isMapAndTable, jboolean hasPosition, jdouble lat, jdouble lon)
{
@ -321,7 +321,7 @@ extern "C"
}
}
JNIEXPORT void JNICALL Java_app_organicmaps_search_SearchEngine_nativeRunSearchMaps(
JNIEXPORT void JNICALL Java_app_organicmaps_sdk_search_SearchEngine_nativeRunSearchMaps(
JNIEnv * env, jclass clazz, jbyteArray bytes, jstring lang, jlong timestamp)
{
storage::DownloaderSearchParams params{
@ -334,7 +334,7 @@ extern "C"
g_queryTimestamp = timestamp;
}
JNIEXPORT jboolean JNICALL Java_app_organicmaps_search_SearchEngine_nativeRunSearchInBookmarks(
JNIEXPORT jboolean JNICALL Java_app_organicmaps_sdk_search_SearchEngine_nativeRunSearchInBookmarks(
JNIEnv * env, jclass clazz, jbyteArray query, jlong catId, jlong timestamp)
{
search::BookmarksSearchParams params{
@ -350,25 +350,25 @@ extern "C"
}
JNIEXPORT void JNICALL
Java_app_organicmaps_search_SearchEngine_nativeShowResult(JNIEnv * env, jclass clazz, jint index)
Java_app_organicmaps_sdk_search_SearchEngine_nativeShowResult(JNIEnv * env, jclass clazz, jint index)
{
g_framework->NativeFramework()->ShowSearchResult(g_results[index]);
}
JNIEXPORT void JNICALL
Java_app_organicmaps_search_SearchEngine_nativeCancelInteractiveSearch(JNIEnv * env, jclass clazz)
Java_app_organicmaps_sdk_search_SearchEngine_nativeCancelInteractiveSearch(JNIEnv * env, jclass clazz)
{
g_framework->NativeFramework()->GetSearchAPI().CancelSearch(search::Mode::Viewport);
}
JNIEXPORT void JNICALL
Java_app_organicmaps_search_SearchEngine_nativeCancelEverywhereSearch(JNIEnv * env, jclass clazz)
Java_app_organicmaps_sdk_search_SearchEngine_nativeCancelEverywhereSearch(JNIEnv * env, jclass clazz)
{
g_framework->NativeFramework()->GetSearchAPI().CancelSearch(search::Mode::Everywhere);
}
JNIEXPORT void JNICALL
Java_app_organicmaps_search_SearchEngine_nativeCancelAllSearches(JNIEnv * env, jclass clazz)
Java_app_organicmaps_sdk_search_SearchEngine_nativeCancelAllSearches(JNIEnv * env, jclass clazz)
{
g_framework->NativeFramework()->GetSearchAPI().CancelAllSearches();
}

View file

@ -0,0 +1,37 @@
#include "app/organicmaps/Framework.hpp"
#include "app/organicmaps/core/jni_helper.hpp"
#include "app/organicmaps/core/jni_java_methods.hpp"
#include "search/result.hpp"
using SearchRequest = search::QuerySaver::SearchRequest;
extern "C"
{
JNIEXPORT void JNICALL Java_app_organicmaps_sdk_search_SearchRecents_nativeGetList(JNIEnv * env, jclass, jobject result)
{
auto const & items = g_framework->NativeFramework()->GetSearchAPI().GetLastSearchQueries();
if (items.empty())
return;
auto const listAddMethod = jni::ListBuilder::Instance(env).m_add;
for (SearchRequest const & item : items)
{
jni::TScopedLocalRef str(env, jni::ToJavaString(env, item.second));
env->CallBooleanMethod(result, listAddMethod, str.get());
}
}
JNIEXPORT void JNICALL Java_app_organicmaps_sdk_search_SearchRecents_nativeAdd(JNIEnv * env, jclass, jstring locale,
jstring query)
{
SearchRequest const sr(jni::ToNativeString(env, locale), jni::ToNativeString(env, query));
g_framework->NativeFramework()->GetSearchAPI().SaveSearchQuery(sr);
}
JNIEXPORT void JNICALL Java_app_organicmaps_sdk_search_SearchRecents_nativeClear(JNIEnv * env, jclass)
{
g_framework->NativeFramework()->GetSearchAPI().ClearSearchHistory();
}
}

View file

@ -49,6 +49,12 @@ Java_app_organicmaps_util_StringUtils_nativeFilterContainsNormalized(JNIEnv * en
return jni::ToJavaStringArray(env, filtered);
}
JNIEXPORT jint JNICALL Java_app_organicmaps_util_StringUtils_nativeFormatSpeed(
JNIEnv * env, jclass thiz, jdouble metersPerSecond)
{
return measurement_utils::FormatSpeed(metersPerSecond, measurement_utils::GetMeasurementUnits());
}
JNIEXPORT jobject JNICALL Java_app_organicmaps_util_StringUtils_nativeFormatSpeedAndUnits(
JNIEnv * env, jclass thiz, jdouble metersPerSecond)
{

View file

@ -390,7 +390,7 @@ public class DownloadResourcesLegacyActivity extends BaseMwmFragmentActivity
mProgress.setProgressCompat(0, true);
mCountryDownloadListenerSlot = MapManager.nativeSubscribe(mCountryDownloadListener);
MapManager.nativeDownload(mCurrentCountry);
MapManager.startDownload(mCurrentCountry);
setAction(PROCEED_TO_MAP);
}
else

View file

@ -333,11 +333,20 @@ public class Framework
public static native int nativeGetBestRouter(double srcLat, double srcLon,
double dstLat, double dstLon);
public static void addRoutePoint(RouteMarkData point)
{
Framework.nativeAddRoutePoint(point.mTitle, point.mSubtitle, point.mPointType,
point.mIntermediateIndex, point.mIsMyPosition,
point.mLat, point.mLon);
}
public static native void nativeAddRoutePoint(String title, String subtitle,
@RoutePointInfo.RouteMarkType int markType,
int intermediateIndex, boolean isMyPosition,
double lat, double lon);
public static native void nativeRemoveRoutePoints();
public static native void nativeRemoveRoutePoint(@RoutePointInfo.RouteMarkType int markType,
int intermediateIndex);
@ -346,6 +355,9 @@ public class Framework
public static native boolean nativeCouldAddIntermediatePoint();
@NonNull
public static native RouteMarkData[] nativeGetRoutePoints();
public static native void nativeMoveRoutePoint(int currentIndex, int targetIndex);
@NonNull
public static native TransitRouteInfo nativeGetTransitRouteInfo();
/**

View file

@ -77,7 +77,7 @@ import app.organicmaps.maplayer.MapButtonsViewModel;
import app.organicmaps.maplayer.ToggleMapLayerFragment;
import app.organicmaps.maplayer.isolines.IsolinesManager;
import app.organicmaps.maplayer.isolines.IsolinesState;
import app.organicmaps.maplayer.subway.SubwayManager;
import app.organicmaps.routing.ManageRouteBottomSheet;
import app.organicmaps.routing.NavigationController;
import app.organicmaps.routing.NavigationService;
import app.organicmaps.routing.RoutePointInfo;
@ -89,7 +89,7 @@ import app.organicmaps.routing.RoutingPlanFragment;
import app.organicmaps.routing.RoutingPlanInplaceController;
import app.organicmaps.search.FloatingSearchToolbarController;
import app.organicmaps.search.SearchActivity;
import app.organicmaps.search.SearchEngine;
import app.organicmaps.sdk.search.SearchEngine;
import app.organicmaps.search.SearchFragment;
import app.organicmaps.settings.DrivingOptionsActivity;
import app.organicmaps.settings.RoadType;
@ -150,6 +150,7 @@ public class MwmActivity extends BaseMwmFragmentActivity
public static final String EXTRA_TRACK_ID = "track_id";
public static final String EXTRA_UPDATE_THEME = "update_theme";
private static final String EXTRA_CONSUMED = "mwm.extra.intent.processed";
private boolean mPreciseLocationDialogShown = false;
private static final String[] DOCKED_FRAGMENTS = { SearchFragment.class.getName(),
DownloaderFragment.class.getName(),
@ -157,7 +158,11 @@ public class MwmActivity extends BaseMwmFragmentActivity
EditorHostFragment.class.getName(),
ReportFragment.class.getName() };
public static final int REQ_CODE_DRIVING_OPTIONS = 6;
public final ActivityResultLauncher<Intent> startDrivingOptionsForResult = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), activityResult ->
{
if( activityResult.getResultCode() == Activity.RESULT_OK)
rebuildLastRoute();
});
private static final String MAIN_MENU_ID = "MAIN_MENU_BOTTOM_SHEET";
private static final String LAYERS_MENU_ID = "LAYERS_MENU_BOTTOM_SHEET";
@ -229,6 +234,8 @@ public class MwmActivity extends BaseMwmFragmentActivity
@NonNull
private DisplayManager mDisplayManager;
ManageRouteBottomSheet mManageRouteBottomSheet;
private boolean mRemoveDisplayListener = true;
private int mLastUiMode = Configuration.UI_MODE_TYPE_UNDEFINED;
@ -586,14 +593,14 @@ public class MwmActivity extends BaseMwmFragmentActivity
ViewCompat.setOnApplyWindowInsetsListener(mPointChooser, (view, windowInsets) -> {
UiUtils.setViewInsetsPaddingBottom(mPointChooser, windowInsets);
UiUtils.setViewInsetsPaddingNoBottom(mPointChooserToolbar, windowInsets);
final int trackRecorderOffset = TrackRecorder.nativeIsTrackRecordingEnabled() ? UiUtils.dimen(this, R.dimen.map_button_size) : 0;
mNavBarHeight = isFullscreen() ? 0 : windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()).bottom;
// For the first loading, set compass top margin to status bar size
// The top inset will be then be updated by the routing controller
if (mCurrentWindowInsets == null)
updateCompassOffset(windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()).top, windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()).right);
else
updateCompassOffset(-1, windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()).right);
{
updateCompassOffset(trackRecorderOffset + windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()).top, windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()).right);
}
refreshLightStatusBar();
updateBottomWidgetsOffset(windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()).left);
mCurrentWindowInsets = windowInsets;
@ -614,7 +621,7 @@ public class MwmActivity extends BaseMwmFragmentActivity
if (!mIsTabletLayout)
{
mRoutingPlanInplaceController = new RoutingPlanInplaceController(this, this, this);
mRoutingPlanInplaceController = new RoutingPlanInplaceController(this, startDrivingOptionsForResult, this, this);
removeCurrentFragment(false);
}
@ -691,6 +698,16 @@ public class MwmActivity extends BaseMwmFragmentActivity
}
}
/** Hides/shows UI while keeping state
* @param isUiHidden True to hide the UI
**/
public void hideOrShowUIWithoutClosingPlacePage(boolean isUiHidden)
{
// Used instead of closeBottomSheet to preserve state and hide instantly
UiUtils.showIf(!isUiHidden, findViewById(R.id.place_page_container_fragment));
mMapButtonsViewModel.setButtonsHidden(isUiHidden);
}
private void showSearchToolbar()
{
mSearchController.show();
@ -800,6 +817,7 @@ public class MwmActivity extends BaseMwmFragmentActivity
showBottomSheet(MAIN_MENU_ID);
}
case help -> showHelp();
case trackRecordingStatus -> showTrackSaveDialog();
}
}
@ -1018,18 +1036,6 @@ public class MwmActivity extends BaseMwmFragmentActivity
mPowerSaveDisclaimerShown = savedInstanceState.getBoolean(POWER_SAVE_DISCLAIMER_SHOWN, false);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
super.onActivityResult(requestCode, resultCode, data);
if (resultCode != Activity.RESULT_OK)
return;
if (requestCode == REQ_CODE_DRIVING_OPTIONS)
rebuildLastRoute();
}
private void rebuildLastRoute()
{
RoutingController.get().attach(this);
@ -1311,6 +1317,16 @@ public class MwmActivity extends BaseMwmFragmentActivity
Framework.nativeGetChoosePositionMode() == Framework.ChoosePositionMode.NONE;
}
@Override
public boolean dispatchGenericMotionEvent(MotionEvent event) {
if (event.getActionMasked() == MotionEvent.ACTION_SCROLL) {
int exponent = event.getAxisValue(MotionEvent.AXIS_VSCROLL) < 0 ? -1 : 1;
Map.onScale(Math.pow(1.7f, exponent), event.getX(), event.getY(), true);
return true;
}
return super.onGenericMotionEvent(event);
}
@Override
public boolean onTouch(View view, MotionEvent event)
{
@ -1488,14 +1504,30 @@ public class MwmActivity extends BaseMwmFragmentActivity
if (mCurrentWindowInsets == null) {
return;
}
int offset = mCurrentWindowInsets.getInsets(WindowInsetsCompat.Type.systemBars()).top;
int offsetY = mCurrentWindowInsets.getInsets(WindowInsetsCompat.Type.systemBars()).top;
int offsetX = mCurrentWindowInsets.getInsets(WindowInsetsCompat.Type.systemBars()).right;
if (show && mRoutingPlanInplaceController != null)
{
final int height = mRoutingPlanInplaceController.calcHeight();
if (height != 0)
offset = height;
offsetY = height;
}
updateCompassOffset(offset);
final int orientation = getResources().getConfiguration().orientation;
final boolean isTrackRecordingEnabled = TrackRecorder.nativeIsTrackRecordingEnabled();
if (isTrackRecordingEnabled && (orientation != Configuration.ORIENTATION_LANDSCAPE))
offsetY += UiUtils.dimen(this, R.dimen.map_button_size);
if (orientation == Configuration.ORIENTATION_LANDSCAPE)
{
if (show)
{
final boolean isSmallScreen = UiUtils.getDisplayTotalHeight(this) < UiUtils.dimen(this, R.dimen.dp_400);
if (!isSmallScreen || TrackRecorder.nativeIsTrackRecordingEnabled())
offsetX += UiUtils.dimen(this, R.dimen.map_button_size);
}
else if (isTrackRecordingEnabled)
offsetY += UiUtils.dimen(this, R.dimen.map_button_size);
}
updateCompassOffset(offsetY, offsetX);
}
@Override
@ -1662,12 +1694,6 @@ public class MwmActivity extends BaseMwmFragmentActivity
mRoutingPlanInplaceController.showDrivingOptionView();
}
@Override
public boolean isSubwayEnabled()
{
return SubwayManager.from(this).isEnabled();
}
@Override
public void onCommonBuildError(int lastResultCode, @NonNull String[] lastMissingMaps)
{
@ -1683,7 +1709,7 @@ public class MwmActivity extends BaseMwmFragmentActivity
mAlertDialog = new MaterialAlertDialogBuilder(this, R.style.MwmTheme_AlertDialog)
.setTitle(R.string.unable_to_calc_alert_title)
.setMessage(R.string.unable_to_calc_alert_subtitle)
.setPositiveButton(R.string.settings, (dialog, which) -> DrivingOptionsActivity.start(this))
.setPositiveButton(R.string.settings, (dialog, which) -> DrivingOptionsActivity.start(this, startDrivingOptionsForResult))
.setNegativeButton(R.string.cancel, null)
.setOnDismissListener(dialog -> mAlertDialog = null)
.show();
@ -1796,7 +1822,7 @@ public class MwmActivity extends BaseMwmFragmentActivity
}
// Check for any location permissions.
if (!LocationUtils.checkCoarseLocationPermission(this))
if (!LocationUtils.checkLocationPermission(this))
{
Logger.w(LOCATION_TAG, "Permissions ACCESS_COARSE_LOCATION and ACCESS_FINE_LOCATION are not granted");
// Calls onMyPositionModeChanged(NOT_FOLLOW_NO_POSITION).
@ -1934,12 +1960,50 @@ public class MwmActivity extends BaseMwmFragmentActivity
mLocationPermissionRequestedForRecording = false;
if (LocationUtils.checkLocationPermission(this))
{
final boolean hasFineLocationPermission = LocationUtils.checkFineLocationPermission(this);
if (LocationState.getMode() == LocationState.NOT_FOLLOW_NO_POSITION)
LocationState.nativeSwitchToNextMode();
if (requestedForRecording && LocationUtils.checkFineLocationPermission(this))
if (requestedForRecording && hasFineLocationPermission)
startTrackRecording();
if (hasFineLocationPermission)
{
Logger.i(LOCATION_TAG, "ACCESS_FINE_LOCATION permission granted");
}
else
{
Logger.w(LOCATION_TAG, "Only ACCESS_COARSE_LOCATION permission granted");
if (mLocationErrorDialog != null && mLocationErrorDialog.isShowing())
{
Logger.w(LOCATION_TAG, "Don't show 'Precise Location denied' dialog because another dialog is in progress");
return;
}
if (!mPreciseLocationDialogShown)
{
mPreciseLocationDialogShown = true;
final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this, R.style.MwmTheme_AlertDialog)
.setTitle("" + getString(R.string.limited_accuracy))
.setMessage(R.string.precise_location_is_disabled_long_text)
.setNegativeButton(R.string.close, (dialog, which) -> dialog.dismiss())
.setCancelable(true)
.setOnDismissListener(dialog -> mLocationErrorDialog = null);
final Intent intent = Utils.makeSystemLocationSettingIntent(this);
if (intent != null)
{
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
builder.setPositiveButton(R.string.location_settings, (dialog, which) -> startActivity(intent));
}
mLocationErrorDialog = builder.show();
}
else
{
Toast.makeText(this, R.string.precise_location_is_disabled_long_text, Toast.LENGTH_LONG).show();
}
}
return;
}
@ -2090,6 +2154,15 @@ public class MwmActivity extends BaseMwmFragmentActivity
RoutingController.get().start();
}
@Override
public void onManageRouteOpen()
{
// Create and show 'Manage Route' Bottom Sheet panel.
mManageRouteBottomSheet = new ManageRouteBottomSheet();
mManageRouteBottomSheet.setCancelable(false);
mManageRouteBottomSheet.show(getSupportFragmentManager(), "ManageRouteBottomSheet");
}
private boolean requestBatterySaverPermission()
{
if (!PowerManagment.isSystemPowerSaveMode(this))
@ -2267,6 +2340,11 @@ public class MwmActivity extends BaseMwmFragmentActivity
requestPostNotificationsPermission();
if (mCurrentWindowInsets != null)
{
final int offset = mCurrentWindowInsets.getInsets(WindowInsetsCompat.Type.systemBars()).top;
updateCompassOffset(offset + UiUtils.dimen(this, R.dimen.map_button_size));
}
Toast.makeText(this, R.string.track_recording, Toast.LENGTH_SHORT).show();
TrackRecordingService.startForegroundService(getApplicationContext());
mMapButtonsViewModel.setTrackRecorderState(true);
@ -2275,6 +2353,18 @@ public class MwmActivity extends BaseMwmFragmentActivity
private void stopTrackRecording()
{
if (mCurrentWindowInsets != null)
{
int offsetY = mCurrentWindowInsets.getInsets(WindowInsetsCompat.Type.systemBars()).top;
final int offsetX = mCurrentWindowInsets.getInsets(WindowInsetsCompat.Type.systemBars()).right;
if (RoutingController.get().isPlanning() && mRoutingPlanInplaceController != null)
{
final int height = mRoutingPlanInplaceController.calcHeight();
if (height != 0)
offsetY = height;
}
updateCompassOffset(offsetY, offsetX);
}
TrackRecordingService.stopService(getApplicationContext());
mMapButtonsViewModel.setTrackRecorderState(false);
}
@ -2309,15 +2399,15 @@ public class MwmActivity extends BaseMwmFragmentActivity
mAlertDialog = new StackedButtonsDialog.Builder(this)
.setTitle(R.string.track_recording_alert_title)
.setCancelable(false)
// Negative/Positive/Neutral doesn't do not have the usual meaning here.
.setPositiveButton(R.string.continue_recording, (dialog, which) -> {
// Negative/Positive/Neutral do not have their usual meaning here.
.setNegativeButton(R.string.continue_recording, (dialog, which) -> {
mAlertDialog = null;
})
.setNeutralButton(R.string.stop_without_saving, (dialog, which) -> {
stopTrackRecording();
mAlertDialog = null;
})
.setNegativeButton(R.string.save, (dialog, which) -> {
.setPositiveButton(R.string.save, (dialog, which) -> {
saveAndStopTrackRecording();
mAlertDialog = null;
})

View file

@ -16,13 +16,14 @@ import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.ProcessLifecycleOwner;
import java.io.IOException;
import java.lang.ref.WeakReference;
import app.organicmaps.background.OsmUploadWork;
import app.organicmaps.downloader.Android7RootCertificateWorkaround;
import app.organicmaps.downloader.DownloaderNotifier;
import app.organicmaps.bookmarks.data.BookmarkManager;
import app.organicmaps.display.DisplayManager;
import app.organicmaps.downloader.CountryItem;
import app.organicmaps.downloader.MapManager;
import app.organicmaps.downloader.Android7RootCertificateWorkaround;
import app.organicmaps.downloader.DownloaderNotifier;
import app.organicmaps.location.LocationHelper;
import app.organicmaps.location.LocationState;
import app.organicmaps.location.SensorHelper;
@ -33,7 +34,7 @@ import app.organicmaps.maplayer.subway.SubwayManager;
import app.organicmaps.maplayer.traffic.TrafficManager;
import app.organicmaps.routing.NavigationService;
import app.organicmaps.routing.RoutingController;
import app.organicmaps.search.SearchEngine;
import app.organicmaps.sdk.search.SearchEngine;
import app.organicmaps.settings.StoragePathManager;
import app.organicmaps.sound.TtsPlayer;
import app.organicmaps.util.Config;
@ -46,10 +47,6 @@ import app.organicmaps.util.Utils;
import app.organicmaps.util.log.Logger;
import app.organicmaps.util.log.LogsManager;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.List;
public class MwmApplication extends Application implements Application.ActivityLifecycleCallbacks
{
@NonNull
@ -78,9 +75,6 @@ public class MwmApplication extends Application implements Application.ActivityL
private volatile boolean mFrameworkInitialized;
private volatile boolean mPlatformInitialized;
@NonNull
private final MapManager.StorageCallback mStorageCallbacks = new StorageCallbackImpl();
@Nullable
private WeakReference<Activity> mTopActivity;
@ -127,6 +121,9 @@ public class MwmApplication extends Application implements Application.ActivityL
return (MwmApplication) context.getApplicationContext();
}
@NonNull
public static MwmApplication sInstance;
@NonNull
public static SharedPreferences prefs(@NonNull Context context)
{
@ -138,6 +135,9 @@ public class MwmApplication extends Application implements Application.ActivityL
{
super.onCreate();
Logger.i(TAG, "Initializing application");
sInstance = this;
LogsManager.INSTANCE.initFileLogging(this);
Android7RootCertificateWorkaround.initializeIfNeeded(this);
@ -230,8 +230,6 @@ public class MwmApplication extends Application implements Application.ActivityL
nativeInitFramework(onComplete);
MapManager.nativeSubscribe(mStorageCallbacks);
initNativeStrings();
ThemeSwitcher.INSTANCE.initialize(this);
SearchEngine.INSTANCE.initialize();
@ -363,25 +361,4 @@ public class MwmApplication extends Application implements Application.ActivityL
mLocationHelper.stop();
}
}
private class StorageCallbackImpl implements MapManager.StorageCallback
{
@Override
public void onStatusChanged(List<MapManager.StorageCallbackData> data)
{
for (MapManager.StorageCallbackData item : data)
if (item.isLeafNode && item.newStatus == CountryItem.STATUS_FAILED)
{
if (MapManager.nativeIsAutoretryFailed())
{
DownloaderNotifier.notifyDownloadFailed(MwmApplication.this, item.countryId);
}
return;
}
}
@Override
public void onProgress(String countryId, long localSize, long remoteSize) {}
}
}

View file

@ -91,7 +91,7 @@ public class SplashActivity extends AppCompatActivity
super.onResume();
if (mCanceled)
return;
if (!Config.isLocationRequested() && !LocationUtils.checkCoarseLocationPermission(this))
if (!Config.isLocationRequested() && !LocationUtils.checkLocationPermission(this))
{
Logger.d(TAG, "Requesting location permissions");
mPermissionRequest.launch(new String[]{

View file

@ -142,7 +142,7 @@ public class BookmarkCategoriesAdapter extends BaseBookmarkCategoryAdapter<Recyc
case TYPE_ACTION_ADD ->
{
Holders.GeneralViewHolder generalViewHolder = (Holders.GeneralViewHolder) holder;
generalViewHolder.getImage().setImageResource(R.drawable.ic_import);
generalViewHolder.getImage().setImageResource(R.drawable.ic_add_list);
generalViewHolder.getText().setText(R.string.bookmarks_create_new_group);
}
case TYPE_ACTION_IMPORT ->

View file

@ -15,6 +15,7 @@ import android.view.View;
import android.widget.Toast;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.CallSuper;
import androidx.annotation.LayoutRes;
import androidx.annotation.NonNull;
@ -57,9 +58,6 @@ public class BookmarkCategoriesFragment extends BaseMwmRecyclerFragment<Bookmark
{
private static final String TAG = BookmarkCategoriesFragment.class.getSimpleName();
static final int REQ_CODE_DELETE_CATEGORY = 102;
static final int REQ_CODE_IMPORT_DIRECTORY = 103;
private static final int MAX_CATEGORY_NAME_LENGTH = 60;
public static final String BOOKMARKS_CATEGORIES_MENU_ID = "BOOKMARKS_CATEGORIES_BOTTOM_SHEET";
@ -75,6 +73,22 @@ public class BookmarkCategoriesFragment extends BaseMwmRecyclerFragment<Bookmark
@NonNull
private DataChangedListener mCategoriesAdapterObserver;
private final ActivityResultLauncher<Intent> startBookmarkListForResult = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), activityResult -> {
if( activityResult.getResultCode() == Activity.RESULT_OK)
onDeleteActionSelected(getSelectedCategory());
});
private final ActivityResultLauncher<Intent> startImportDirectoryForResult = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), activityResult ->
{
if( activityResult.getResultCode() == Activity.RESULT_OK)
onImportDirectoryResult(activityResult.getData());
});
private final ActivityResultLauncher<Intent> startBookmarkSettingsForResult = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), activityResult -> {
// not handled at the moment
});
@Override
@LayoutRes
protected int getLayoutRes()
@ -259,7 +273,7 @@ public class BookmarkCategoriesFragment extends BaseMwmRecyclerFragment<Bookmark
PackageManager packageManager = requireActivity().getPackageManager();
if (intent.resolveActivity(packageManager) != null)
startActivityForResult(intent, REQ_CODE_IMPORT_DIRECTORY);
startImportDirectoryForResult.launch(intent);
else
showNoFileManagerError();
}
@ -275,7 +289,7 @@ public class BookmarkCategoriesFragment extends BaseMwmRecyclerFragment<Bookmark
public void onItemClick(@NonNull View v, @NonNull BookmarkCategory category)
{
mSelectedCategory = category;
BookmarkListActivity.startForResult(this, category);
BookmarkListActivity.startForResult(this, startBookmarkListForResult, category);
}
@Override
@ -303,54 +317,42 @@ public class BookmarkCategoriesFragment extends BaseMwmRecyclerFragment<Bookmark
private void onSettingsActionSelected(@NonNull BookmarkCategory category)
{
BookmarkCategorySettingsActivity.startForResult(this, category);
BookmarkCategorySettingsActivity.startForResult(this, startBookmarkSettingsForResult, category);
}
@Override
public final void onActivityResult(int requestCode, int resultCode, Intent data)
private void onImportDirectoryResult(Intent data)
{
super.onActivityResult(requestCode, resultCode, data);
if (resultCode != Activity.RESULT_OK)
return;
switch (requestCode)
{
case REQ_CODE_DELETE_CATEGORY -> onDeleteActionSelected(getSelectedCategory());
case REQ_CODE_IMPORT_DIRECTORY ->
{
if (data == null)
throw new AssertionError("Data is null");
if (data == null)
throw new AssertionError("Data is null");
final Context context = requireActivity();
final Uri rootUri = data.getData();
final ProgressDialog dialog = new ProgressDialog(context, R.style.MwmTheme_ProgressDialog);
dialog.setMessage(getString(R.string.wait_several_minutes));
dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
dialog.setIndeterminate(true);
dialog.setCancelable(false);
dialog.show();
Logger.d(TAG, "Importing bookmarks from " + rootUri);
MwmApplication app = MwmApplication.from(context);
final File tempDir = new File(StorageUtils.getTempPath(app));
final ContentResolver resolver = context.getContentResolver();
ThreadPool.getStorage().execute(() -> {
AtomicInteger found = new AtomicInteger(0);
StorageUtils.listContentProviderFilesRecursively(
final Context context = requireActivity();
final Uri rootUri = data.getData();
final ProgressDialog dialog = new ProgressDialog(context, R.style.MwmTheme_ProgressDialog);
dialog.setMessage(getString(R.string.wait_several_minutes));
dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
dialog.setIndeterminate(true);
dialog.setCancelable(false);
dialog.show();
Logger.d(TAG, "Importing bookmarks from " + rootUri);
MwmApplication app = MwmApplication.from(context);
final File tempDir = new File(StorageUtils.getTempPath(app));
final ContentResolver resolver = context.getContentResolver();
ThreadPool.getStorage().execute(() -> {
AtomicInteger found = new AtomicInteger(0);
StorageUtils.listContentProviderFilesRecursively(
resolver, rootUri, uri -> {
if (BookmarkManager.INSTANCE.importBookmarksFile(resolver, uri, tempDir))
found.incrementAndGet();
});
UiThread.run(() -> {
if (dialog.isShowing())
dialog.dismiss();
int found_val = found.get();
String message = context.getResources().getQuantityString(
UiThread.run(() -> {
if (dialog.isShowing())
dialog.dismiss();
int found_val = found.get();
String message = context.getResources().getQuantityString(
R.plurals.bookmarks_detect_message, found_val, found_val);
Toast.makeText(requireContext(), message, Toast.LENGTH_LONG).show();
});
});
}
default -> throw new AssertionError("Invalid requestCode: " + requestCode);
}
Toast.makeText(requireContext(), message, Toast.LENGTH_LONG).show();
});
});
}
@Override

View file

@ -2,6 +2,7 @@ package app.organicmaps.bookmarks;
import android.content.Intent;
import androidx.activity.result.ActivityResultLauncher;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
@ -11,7 +12,6 @@ import app.organicmaps.bookmarks.data.BookmarkCategory;
public class BookmarkCategorySettingsActivity extends BaseMwmFragmentActivity
{
public static final int REQUEST_CODE = 107;
public static final String EXTRA_BOOKMARK_CATEGORY = "bookmark_category";
@Override
@ -32,11 +32,11 @@ public class BookmarkCategorySettingsActivity extends BaseMwmFragmentActivity
return BookmarkCategorySettingsFragment.class;
}
public static void startForResult(@NonNull Fragment fragment,
public static void startForResult(@NonNull Fragment fragment, ActivityResultLauncher<Intent> startBookmarkSettingsForResult,
@NonNull BookmarkCategory category)
{
android.content.Intent intent = new Intent(fragment.requireActivity(), BookmarkCategorySettingsActivity.class)
.putExtra(EXTRA_BOOKMARK_CATEGORY, category);
fragment.startActivityForResult(intent, REQUEST_CODE);
startBookmarkSettingsForResult.launch(intent);
}
}

View file

@ -3,6 +3,7 @@ package app.organicmaps.bookmarks;
import android.content.Intent;
import android.os.Bundle;
import androidx.activity.result.ActivityResultLauncher;
import androidx.annotation.CallSuper;
import androidx.annotation.NonNull;
import androidx.annotation.StyleRes;
@ -57,11 +58,11 @@ public class BookmarkListActivity extends BaseToolbarActivity
return R.layout.bookmarks_activity;
}
static void startForResult(@NonNull Fragment fragment, @NonNull BookmarkCategory category)
static void startForResult(@NonNull Fragment fragment, ActivityResultLauncher<Intent> startBookmarkListForResult, @NonNull BookmarkCategory category)
{
Bundle args = new Bundle();
Intent intent = new Intent(fragment.requireActivity(), BookmarkListActivity.class);
intent.putExtra(BookmarksListFragment.EXTRA_CATEGORY, category);
fragment.startActivityForResult(intent, BookmarkCategoriesFragment.REQ_CODE_DELETE_CATEGORY);
startBookmarkListForResult.launch(intent);
}
}

View file

@ -14,6 +14,7 @@ import android.view.ViewGroup;
import android.widget.ImageView;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.CallSuper;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@ -38,8 +39,8 @@ import app.organicmaps.bookmarks.data.KmlFileType;
import app.organicmaps.bookmarks.data.SortedBlock;
import app.organicmaps.bookmarks.data.Track;
import app.organicmaps.location.LocationHelper;
import app.organicmaps.search.NativeBookmarkSearchListener;
import app.organicmaps.search.SearchEngine;
import app.organicmaps.sdk.search.BookmarkSearchListener;
import app.organicmaps.sdk.search.SearchEngine;
import app.organicmaps.util.Graphics;
import app.organicmaps.util.SharingUtils;
import app.organicmaps.util.UiUtils;
@ -61,7 +62,7 @@ public class BookmarksListFragment extends BaseMwmRecyclerFragment<ConcatAdapter
implements BookmarkManager.BookmarksSharingListener,
BookmarkManager.BookmarksSortingListener,
BookmarkManager.BookmarksLoadingListener,
NativeBookmarkSearchListener,
BookmarkSearchListener,
ChooseBookmarksSortingTypeFragment.ChooseSortingTypeListener,
MenuBottomSheetFragment.MenuBottomSheetInterface
{
@ -74,6 +75,15 @@ public class BookmarksListFragment extends BaseMwmRecyclerFragment<ConcatAdapter
private static final String OPTIONS_MENU_ID = "OPTIONS_MENU_BOTTOM_SHEET";
private ActivityResultLauncher<SharingUtils.SharingIntent> shareLauncher;
private final ActivityResultLauncher<Intent> startBookmarkListForResult = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), activityResult -> {
System.out.println("resultCode: " + activityResult.getResultCode());
handleActivityResult();
});
private final ActivityResultLauncher<Intent> startBookmarkSettingsForResult = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), activityResult -> {
System.out.println("resultCode: " + activityResult.getResultCode());
handleActivityResult();
});
@SuppressWarnings("NotNullFieldNotInitialized")
@NonNull
@ -140,7 +150,9 @@ public class BookmarksListFragment extends BaseMwmRecyclerFragment<ConcatAdapter
BookmarkCollectionAdapter adapter = new BookmarkCollectionAdapter(getCategoryOrThrow(),
mCategoryItems);
adapter.setOnClickListener((v, item) -> BookmarkListActivity.startForResult(this, item));
adapter.setOnClickListener((v, item) -> {
BookmarkListActivity.startForResult(this, startBookmarkListForResult, item);
});
return adapter;
}
@ -756,7 +768,7 @@ public class BookmarksListFragment extends BaseMwmRecyclerFragment<ConcatAdapter
private void onSettingsOptionSelected()
{
BookmarkCategorySettingsActivity.startForResult(this, mCategoryDataSource.getData());
BookmarkCategorySettingsActivity.startForResult(this, startBookmarkSettingsForResult, mCategoryDataSource.getData());
}
private void onDeleteOptionSelected()
@ -795,21 +807,25 @@ public class BookmarksListFragment extends BaseMwmRecyclerFragment<ConcatAdapter
{
ArrayList<MenuBottomSheetItem> items = new ArrayList<>();
items.add(new MenuBottomSheetItem(R.string.edit, R.drawable.ic_edit, this::onTrackEditActionSelected));
items.add(new MenuBottomSheetItem(R.string.export_file, R.drawable.ic_file_kmz, () -> onShareTrackSelected(track.getTrackId(), KmlFileType.Text)));
items.add(new MenuBottomSheetItem(R.string.export_file_gpx, R.drawable.ic_file_gpx, () -> onShareTrackSelected(track.getTrackId(), KmlFileType.Gpx)));
items.add(new MenuBottomSheetItem(R.string.delete, R.drawable.ic_delete, () -> onDeleteTrackSelected(track.getTrackId())));
return items;
}
private void onShareTrackSelected(long trackId, KmlFileType kmlFileType)
{
BookmarksSharingHelper.INSTANCE.prepareTrackForSharing(requireActivity(), trackId, kmlFileType);
}
@Override
public void onPreparedFileForSharing(@NonNull BookmarkSharingResult result)
{
BookmarksSharingHelper.INSTANCE.onPreparedFileForSharing(requireActivity(), shareLauncher, result);
}
@Override
@SuppressWarnings("deprecation") // https://github.com/organicmaps/organicmaps/issues/3630
public void onActivityResult(int requestCode, int resultCode, Intent data)
private void handleActivityResult()
{
super.onActivityResult(requestCode, resultCode, data);
getBookmarkListAdapter().notifyDataSetChanged();
ActionBar actionBar = ((AppCompatActivity) requireActivity()).getSupportActionBar();
actionBar.setTitle(mCategoryDataSource.getData().getName());

View file

@ -36,6 +36,12 @@ public enum BookmarksSharingHelper
BookmarkManager.INSTANCE.prepareCategoriesForSharing(new long[]{catId}, kmlFileType);
}
public void prepareTrackForSharing(@NonNull Activity context, long trackId, KmlFileType kmlFileType)
{
showProgressDialog(context);
BookmarkManager.INSTANCE.prepareTrackForSharing(trackId, kmlFileType);
}
private void showProgressDialog(@NonNull Activity context)
{
mProgressDialog = new ProgressDialog(context, R.style.MwmTheme_ProgressDialog);

View file

@ -11,7 +11,7 @@ import androidx.core.os.ParcelCompat;
import app.organicmaps.Framework;
import app.organicmaps.routing.RoutePointInfo;
import app.organicmaps.search.Popularity;
import app.organicmaps.sdk.search.Popularity;
import app.organicmaps.util.Constants;
// TODO consider refactoring to remove hack with MapObject unmarshalling itself and Bookmark at the same time.

View file

@ -527,6 +527,11 @@ public enum BookmarkManager
nativePrepareFileForSharing(catIds, kmlFileType.ordinal());
}
public void prepareTrackForSharing(long trackId, KmlFileType kmlFileType)
{
nativePrepareTrackFileForSharing(trackId, kmlFileType.ordinal());
}
public void setNotificationsEnabled(boolean enabled)
{
nativeSetNotificationsEnabled(enabled);
@ -806,6 +811,8 @@ public enum BookmarkManager
private static native void nativePrepareFileForSharing(long[] catIds, int kmlFileType);
private static native void nativePrepareTrackFileForSharing(long trackId, int kmlFileType);
private static native boolean nativeIsCategoryEmpty(long catId);
private static native void nativeSetNotificationsEnabled(boolean enabled);

View file

@ -12,7 +12,7 @@ import androidx.core.os.ParcelCompat;
import app.organicmaps.Framework;
import app.organicmaps.routing.RoutePointInfo;
import app.organicmaps.search.Popularity;
import app.organicmaps.sdk.search.Popularity;
import app.organicmaps.util.Utils;
import app.organicmaps.widget.placepage.PlacePageData;

View file

@ -1,6 +1,5 @@
package app.organicmaps.car;
import android.app.Notification;
import android.content.ComponentName;
import android.content.Intent;
import android.net.Uri;
@ -23,7 +22,6 @@ import androidx.lifecycle.LifecycleOwner;
import app.organicmaps.BuildConfig;
import app.organicmaps.R;
import app.organicmaps.api.Const;
import app.organicmaps.routing.NavigationService;
public final class CarAppService extends androidx.car.app.CarAppService
{
@ -53,20 +51,7 @@ public final class CarAppService extends androidx.car.app.CarAppService
public Session onCreateSession(@Nullable SessionInfo sessionInfo)
{
createNotificationChannel();
startForeground(NOTIFICATION_ID, getNotification());
final CarAppSession carAppSession = new CarAppSession(sessionInfo);
carAppSession.getLifecycle().addObserver(new DefaultLifecycleObserver()
{
@Override
public void onDestroy(@NonNull LifecycleOwner owner)
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
stopForeground(STOP_FOREGROUND_REMOVE);
else
stopForeground(true);
}
});
return carAppSession;
return new CarAppSession(sessionInfo);
}
@NonNull
@ -104,13 +89,4 @@ public final class CarAppService extends androidx.car.app.CarAppService
.build();
notificationManager.createNotificationChannel(notificationChannel);
}
@NonNull
private Notification getNotification()
{
return NavigationService.getNotificationBuilder(this)
.setChannelId(ANDROID_AUTO_NOTIFICATION_CHANNEL_ID)
.setContentTitle(getString(R.string.aa_connected_to_car_notification_title))
.build();
}
}

View file

@ -109,7 +109,7 @@ class DownloaderScreen extends BaseScreen
for (final var item : mMissingMaps.entrySet())
{
item.getValue().update();
MapManager.nativeDownload(item.getKey());
MapManager.startDownload(item.getKey());
}
}
@ -150,12 +150,11 @@ class DownloaderScreen extends BaseScreen
return getCarContext().getString(R.string.downloader_loading_ios);
final long downloadedSize = getDownloadedSize();
final float progress = (float) downloadedSize / mTotalSize * 100;
final String progressPercent = StringUtils.formatPercent((double) downloadedSize / mTotalSize);
final String totalSizeStr = StringUtils.getFileSizeString(getCarContext(), mTotalSize);
final String downloadedSizeStr = StringUtils.getFileSizeString(getCarContext(), downloadedSize);
return StringUtils.formatUsingSystemLocale("%.2f%%\n%s",
progress, downloadedSizeStr + " / " + totalSizeStr);
return progressPercent + "\n" + downloadedSizeStr + " / " + totalSizeStr;
}
private long getDownloadedSize()

View file

@ -113,7 +113,7 @@ public class RequestPermissionsScreenWithNotification extends BaseScreen impleme
.setOngoing(true)
.setShowWhen(false)
.setOnlyAlertOnce(true)
.setSmallIcon(R.drawable.ic_my_location)
.setSmallIcon(R.drawable.ic_location_crosshair)
.setColor(ContextCompat.getColor(getCarContext(), R.color.notification))
.setContentTitle(getCarContext().getString(R.string.aa_request_permission_notification))
.setContentIntent(pendingIntent);

View file

@ -23,13 +23,13 @@ import app.organicmaps.car.SurfaceRenderer;
import app.organicmaps.car.screens.base.BaseMapScreen;
import app.organicmaps.car.util.UiHelpers;
import app.organicmaps.location.LocationHelper;
import app.organicmaps.search.NativeSearchListener;
import app.organicmaps.search.SearchEngine;
import app.organicmaps.search.SearchRecents;
import app.organicmaps.search.SearchResult;
import app.organicmaps.sdk.search.SearchListener;
import app.organicmaps.sdk.search.SearchEngine;
import app.organicmaps.sdk.search.SearchRecents;
import app.organicmaps.sdk.search.SearchResult;
import app.organicmaps.util.Language;
public class SearchOnMapScreen extends BaseMapScreen implements NativeSearchListener
public class SearchOnMapScreen extends BaseMapScreen implements SearchListener
{
private final int MAX_RESULTS_SIZE;

View file

@ -21,13 +21,13 @@ import app.organicmaps.bookmarks.data.MapObject;
import app.organicmaps.car.SurfaceRenderer;
import app.organicmaps.car.screens.base.BaseMapScreen;
import app.organicmaps.location.LocationHelper;
import app.organicmaps.search.NativeSearchListener;
import app.organicmaps.search.SearchEngine;
import app.organicmaps.search.SearchRecents;
import app.organicmaps.search.SearchResult;
import app.organicmaps.sdk.search.SearchListener;
import app.organicmaps.sdk.search.SearchEngine;
import app.organicmaps.sdk.search.SearchRecents;
import app.organicmaps.sdk.search.SearchResult;
import app.organicmaps.util.Language;
public class SearchScreen extends BaseMapScreen implements SearchTemplate.SearchCallback, NativeSearchListener
public class SearchScreen extends BaseMapScreen implements SearchTemplate.SearchCallback, SearchListener
{
private final int MAX_RESULTS_SIZE;

View file

@ -13,7 +13,7 @@ import androidx.car.app.model.ForegroundCarColorSpan;
import app.organicmaps.R;
import app.organicmaps.car.util.Colors;
import app.organicmaps.car.util.RoutingHelpers;
import app.organicmaps.search.SearchResult;
import app.organicmaps.sdk.search.SearchResult;
import java.util.ArrayList;
import java.util.List;

View file

@ -8,7 +8,7 @@ import androidx.car.app.suggestion.model.Suggestion;
import androidx.core.graphics.drawable.IconCompat;
import app.organicmaps.R;
import app.organicmaps.search.SearchRecents;
import app.organicmaps.sdk.search.SearchRecents;
import java.util.ArrayList;
import java.util.List;

View file

@ -1,69 +0,0 @@
package app.organicmaps.dialog;
import android.app.Activity;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.Fragment;
import app.organicmaps.R;
public class ProgressDialogFragment extends DialogFragment
{
private static final String ARG_MESSAGE = "title";
private static final String ARG_CANCELABLE = "cancelable";
private static final String ARG_RETAIN_INSTANCE = "retain_instance";
public ProgressDialogFragment()
{
// Do nothing by default.
}
protected void setCancelResult()
{
Fragment targetFragment = getTargetFragment();
if (targetFragment != null)
targetFragment.onActivityResult(getTargetRequestCode(), Activity.RESULT_CANCELED, null);
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
Bundle args = requireArguments();
setRetainInstance(args.getBoolean(ARG_RETAIN_INSTANCE, true));
setCancelable(args.getBoolean(ARG_CANCELABLE, false));
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable
Bundle savedInstanceState)
{
View view = inflater.inflate(R.layout.indeterminated_progress_dialog, container, false);
Bundle args = requireArguments();
TextView messageView = view.findViewById(R.id.message);
messageView.setText(args.getString(ARG_MESSAGE));
return view;
}
@Override
public void onCancel(DialogInterface dialog)
{
setCancelResult();
}
@Override
public void onDestroyView()
{
if (getDialog() != null && getRetainInstance())
getDialog().setDismissMessage(null);
super.onDestroyView();
}
}

View file

@ -30,7 +30,7 @@ class BottomPanel
public void onClick(View v)
{
final String country = mFragment.getCurrentRoot();
MapManager.warnOn3gUpdate(mFragment.requireActivity(), country, () -> MapManager.nativeUpdate(country));
MapManager.warnOn3gUpdate(mFragment.requireActivity(), country, () -> MapManager.startUpdate(country));
}
};

View file

@ -63,7 +63,9 @@ public final class CountryItem implements Comparable<CountryItem>
public int errorCode;
public boolean present;
// Progress
/**
* This value represents the percentage of download (values span from 0 to 100)
*/
public float progress;
public long downloadedBytes;
public long bytesToDownload;

View file

@ -192,8 +192,7 @@ public class CountrySuggestFragment extends BaseMwmFragment implements View.OnCl
private void updateProgress()
{
String text = StringUtils.formatUsingSystemLocale("%1$s %2$.2f%%", getString(R.string.downloader_downloading),
mDownloadingCountry.progress);
String text = getString(R.string.downloader_downloading) + " " + StringUtils.formatPercent(mDownloadingCountry.progress / 100);
mTvProgress.setText(text);
mWpvDownloadProgress.setProgress(Math.round(mDownloadingCountry.progress));
}

View file

@ -91,7 +91,7 @@ class DownloaderAdapter extends RecyclerView.Adapter<DownloaderAdapter.ViewHolde
item.update();
if (item.status != CountryItem.STATUS_UPDATABLE)
return;
MapManager.warnOn3gUpdate(adapter.mActivity, item.id, () -> MapManager.nativeUpdate(item.id));
MapManager.warnOn3gUpdate(adapter.mActivity, item.id, () -> MapManager.startUpdate(item.id));
}
private void onExploreActionSelected(CountryItem item, DownloaderAdapter adapter)
@ -150,6 +150,7 @@ class DownloaderAdapter extends RecyclerView.Adapter<DownloaderAdapter.ViewHolde
((MwmActivity) adapter.mActivity).closePlacePage();
}
deleteNode(item);
refreshData();
}
private record PathEntry(CountryItem item, boolean myMapsMode, int topPosition, int topOffset)
@ -204,14 +205,9 @@ class DownloaderAdapter extends RecyclerView.Adapter<DownloaderAdapter.ViewHolde
}
}
if (mSearchResultsMode)
for (MapManager.StorageCallbackData item : data)
{
for (MapManager.StorageCallbackData item : data)
updateItem(item.countryId);
}
else
{
refreshData();
updateItem(item.countryId);
}
}
@ -386,12 +382,10 @@ class DownloaderAdapter extends RecyclerView.Adapter<DownloaderAdapter.ViewHolde
}
case CountryItem.STATUS_FAILED ->
{
RetryFailedDownloadConfirmationListener listener =
new RetryFailedDownloadConfirmationListener(mActivity.getApplication());
MapManager.warn3gAndRetry(mActivity, mItem.id, listener);
MapManager.warn3gAndRetry(mActivity, mItem.id, null);
}
case CountryItem.STATUS_UPDATABLE ->
MapManager.warnOn3gUpdate(mActivity, mItem.id, () -> MapManager.nativeUpdate(mItem.id));
MapManager.warnOn3gUpdate(mActivity, mItem.id, () -> MapManager.startUpdate(mItem.id));
default -> throw new IllegalArgumentException("Inappropriate item status: " + mItem.status);
}
}

View file

@ -5,6 +5,8 @@ import android.os.Bundle;
import android.view.View;
import android.view.WindowManager;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.CallSuper;
import androidx.annotation.Keep;
import androidx.annotation.NonNull;
@ -15,8 +17,8 @@ import androidx.recyclerview.widget.RecyclerView;
import app.organicmaps.R;
import app.organicmaps.base.BaseMwmRecyclerFragment;
import app.organicmaps.base.OnBackPressListener;
import app.organicmaps.search.NativeMapSearchListener;
import app.organicmaps.search.SearchEngine;
import app.organicmaps.sdk.search.MapSearchListener;
import app.organicmaps.sdk.search.SearchEngine;
import app.organicmaps.widget.PlaceholderView;
import app.organicmaps.util.bottomsheet.MenuBottomSheetFragment;
import app.organicmaps.util.bottomsheet.MenuBottomSheetItem;
@ -39,6 +41,8 @@ public class DownloaderFragment extends BaseMwmRecyclerFragment<DownloaderAdapte
private int mSubscriberSlot;
final ActivityResultLauncher<Intent> startVoiceRecognitionForResult = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), activityResult -> mToolbarController.onVoiceRecognitionResult(activityResult));
private final RecyclerView.OnScrollListener mScrollListener = new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState)
@ -48,13 +52,13 @@ public class DownloaderFragment extends BaseMwmRecyclerFragment<DownloaderAdapte
}
};
private final NativeMapSearchListener mSearchListener = new NativeMapSearchListener()
private final MapSearchListener mSearchListener = new MapSearchListener()
{
// Called from JNI.
@Keep
@SuppressWarnings("unused")
@Override
public void onMapSearchResults(Result[] results, long timestamp, boolean isLast)
public void onMapSearchResults(@NonNull Result[] results, long timestamp, boolean isLast)
{
if (!mSearchRunning || timestamp != mCurrentSearch)
return;
@ -62,8 +66,8 @@ public class DownloaderFragment extends BaseMwmRecyclerFragment<DownloaderAdapte
List<CountryItem> rs = new ArrayList<>();
for (Result result : results)
{
CountryItem item = CountryItem.fill(result.countryId);
item.searchResultName = result.matchedString;
CountryItem item = CountryItem.fill(result.countryId());
item.searchResultName = result.matchedString();
rs.add(item);
}
@ -210,13 +214,6 @@ public class DownloaderFragment extends BaseMwmRecyclerFragment<DownloaderAdapte
return mAdapter;
}
@Override
@SuppressWarnings("deprecation") // https://github.com/organicmaps/organicmaps/issues/3630
public void onActivityResult(int requestCode, int resultCode, Intent data)
{
super.onActivityResult(requestCode, resultCode, data);
mToolbarController.onActivityResult(requestCode, resultCode, data);
}
@NonNull
@Override

View file

@ -22,18 +22,26 @@ import app.organicmaps.R;
import app.organicmaps.util.StringUtils;
import app.organicmaps.util.log.Logger;
public abstract class DownloaderNotifier
public class DownloaderNotifier
{
private static final String TAG = DownloaderNotifier.class.getSimpleName();
private static final String CHANNEL_ID = "downloader";
private static final int NOTIFICATION_ID = 1;
public static final int NOTIFICATION_ID = 1;
private final Context mContext;
private NotificationCompat.Builder mProgressNotificationBuilder;
public DownloaderNotifier(Context context)
{
mContext = context;
}
public static void createNotificationChannel(@NonNull Context context)
{
final NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
final NotificationChannelCompat channel = new NotificationChannelCompat.Builder(CHANNEL_ID,
NotificationManagerCompat.IMPORTANCE_DEFAULT)
NotificationManagerCompat.IMPORTANCE_LOW)
.setName(context.getString(R.string.notification_channel_downloader))
.setShowBadge(true)
.setVibrationEnabled(false)
@ -42,49 +50,93 @@ public abstract class DownloaderNotifier
notificationManager.createNotificationChannel(channel);
}
public static void notifyDownloadFailed(@NonNull Context context, @Nullable String countryId)
public void notifyDownloadFailed(@Nullable String countryId)
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU &&
ContextCompat.checkSelfPermission(context, POST_NOTIFICATIONS) != PERMISSION_GRANTED)
ContextCompat.checkSelfPermission(mContext, POST_NOTIFICATIONS) != PERMISSION_GRANTED)
{
Logger.w(TAG, "Permission POST_NOTIFICATIONS is not granted, skipping notification");
return;
}
final String title = context.getString(R.string.app_name);
final String title = mContext.getString(R.string.app_name);
final String countryName = MapManager.nativeGetName(countryId);
final String content = context.getString(R.string.download_country_failed, countryName);
final String content = mContext.getString(R.string.download_country_failed, countryName);
final int FLAG_IMMUTABLE = Build.VERSION.SDK_INT < Build.VERSION_CODES.M ? 0 : PendingIntent.FLAG_IMMUTABLE;
final Intent contentIntent = MwmActivity.createShowMapIntent(context, countryId);
contentIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
final PendingIntent contentPendingIntent = PendingIntent.getActivity(context, 0, contentIntent,
PendingIntent.FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE);
var contentPendingIntent = getNotificationPendingIntent(countryId);
final Notification notification = new NotificationCompat.Builder(context, CHANNEL_ID)
final Notification notification = new NotificationCompat.Builder(mContext, CHANNEL_ID)
.setAutoCancel(true)
.setCategory(NotificationCompat.CATEGORY_ERROR)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setSmallIcon(R.drawable.ic_splash)
.setColor(ContextCompat.getColor(context, R.color.notification))
.setColor(ContextCompat.getColor(mContext, R.color.notification))
.setContentTitle(title)
.setContentText(content)
.setShowWhen(true)
.setTicker(getTicker(context, title, content))
.setTicker(getTicker(mContext, title, content))
.setContentIntent(contentPendingIntent)
.setOnlyAlertOnce(true)
.build();
Logger.i(TAG, "Notifying about failed map download");
final NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
final NotificationManagerCompat notificationManager = NotificationManagerCompat.from(mContext);
notificationManager.notify(NOTIFICATION_ID, notification);
}
static void cancelNotification(@NonNull Context context)
public void notifyProgress() {
notifyProgress(null, 0, 0);
}
public void notifyProgress(@Nullable String countryId, int maxProgress, int progress) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU &&
ContextCompat.checkSelfPermission(mContext, POST_NOTIFICATIONS) != PERMISSION_GRANTED)
{
Logger.w(TAG, "Permission POST_NOTIFICATIONS is not granted, skipping notification");
return;
}
NotificationManagerCompat.from(mContext).notify(NOTIFICATION_ID, buildProgressNotification(countryId, maxProgress, progress));
}
@NonNull
public Notification buildProgressNotification()
{
Logger.i(TAG, "Cancelling notification about failed map download");
final NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
notificationManager.cancel(NOTIFICATION_ID);
return buildProgressNotification(null, 0, 0);
}
@NonNull
public Notification buildProgressNotification(@Nullable String countryId, int maxProgress, int progress)
{
var builder = startNotification(countryId);
builder.setProgress(maxProgress, progress, maxProgress == 0);
builder.setContentText("Download in progress");
return builder.build();
}
@NonNull
private NotificationCompat.Builder startNotification(@Nullable String countryId)
{
final String title = mContext.getString(R.string.app_name);
return new NotificationCompat.Builder(mContext, CHANNEL_ID)
.setAutoCancel(true)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setSmallIcon(R.drawable.ic_splash)
.setColor(ContextCompat.getColor(mContext, R.color.notification))
.setShowWhen(true)
.setContentTitle(title)
.setContentIntent(getNotificationPendingIntent(countryId));
}
@NonNull
private PendingIntent getNotificationPendingIntent(@Nullable String countryId) {
final int FLAG_IMMUTABLE = Build.VERSION.SDK_INT < Build.VERSION_CODES.M ? 0 : PendingIntent.FLAG_IMMUTABLE;
final Intent contentIntent = MwmActivity.createShowMapIntent(mContext, countryId);
contentIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
return PendingIntent.getActivity(mContext, 0, contentIntent, PendingIntent.FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE);
}
@NonNull

View file

@ -0,0 +1,142 @@
package app.organicmaps.downloader;
import static android.Manifest.permission.POST_NOTIFICATIONS;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import android.app.ForegroundServiceStartNotAllowedException;
import android.app.Service;
import android.content.Intent;
import android.os.Build;
import android.os.IBinder;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import java.util.List;
import app.organicmaps.MwmApplication;
import app.organicmaps.util.log.Logger;
public class DownloaderService extends Service implements MapManager.StorageCallback
{
private static final String TAG = DownloaderService.class.getSimpleName();
private final DownloaderNotifier mNotifier = new DownloaderNotifier(this);
private int mSubscriptionSlot;
@Override
public void onCreate()
{
super.onCreate();
Logger.i(TAG);
mSubscriptionSlot = MapManager.nativeSubscribe(this);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId)
{
Logger.i(TAG, "Downloading: " + MapManager.nativeIsDownloading());
var notification = mNotifier.buildProgressNotification();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
{
try
{
startForeground(DownloaderNotifier.NOTIFICATION_ID, notification);
} catch (ForegroundServiceStartNotAllowedException e)
{
Logger.e(TAG, "Oops! ForegroundService is not allowed", e);
}
} else
{
startForeground(DownloaderNotifier.NOTIFICATION_ID, notification);
}
return START_NOT_STICKY;
}
@Nullable
@Override
public IBinder onBind(Intent intent)
{
return null;
}
@Override
public void onStatusChanged(List<MapManager.StorageCallbackData> data)
{
var isDownloading = MapManager.nativeIsDownloading();
var hasFailed = hasDownloadFailed(data);
Logger.i(TAG, "Downloading: " + isDownloading + " failure: " + hasFailed);
if (!isDownloading)
{
if (hasFailed)
{
// Detach service from the notification to keep after the service is stopped.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
{
stopForeground(Service.STOP_FOREGROUND_DETACH);
} else
{
stopForeground(false);
}
}
stopSelf();
}
}
@Override
public void onProgress(String countryId, long localSize, long remoteSize)
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU &&
ContextCompat.checkSelfPermission(this, POST_NOTIFICATIONS) != PERMISSION_GRANTED)
{
Logger.w(TAG, "Permission POST_NOTIFICATIONS is not granted, skipping notification");
return;
}
// TODO: How to calculate progress?
mNotifier.notifyProgress();
}
@Override
public void onDestroy()
{
super.onDestroy();
Logger.i(TAG, "onDestroy");
MapManager.nativeUnsubscribe(mSubscriptionSlot);
}
/**
* Start the foreground service to keep the user informed about the status of region downloads.
*/
public static void startForegroundService()
{
Logger.i(TAG);
var context = MwmApplication.sInstance;
ContextCompat.startForegroundService(context, new Intent(context, DownloaderService.class));
}
private boolean hasDownloadFailed(List<MapManager.StorageCallbackData> data)
{
for (MapManager.StorageCallbackData item : data)
{
if (item.isLeafNode && item.newStatus == CountryItem.STATUS_FAILED)
{
if (MapManager.nativeIsAutoretryFailed())
{
mNotifier.notifyDownloadFailed(item.countryId);
return true;
}
}
}
return false;
}
}

View file

@ -58,10 +58,9 @@ class DownloaderToolbarController extends SearchToolbarController
}
@Override
@SuppressWarnings("deprecated") // https://github.com/organicmaps/organicmaps/issues/3630
protected void startVoiceRecognition(Intent intent, int code)
protected void startVoiceRecognition(Intent intent)
{
mFragment.startActivityForResult(intent, code);
mFragment.startVoiceRecognitionForResult.launch(intent);
}
@Override

View file

@ -1,28 +1,22 @@
package app.organicmaps.downloader;
import android.app.Application;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import app.organicmaps.util.Utils;
class ExpandRetryConfirmationListener extends RetryFailedDownloadConfirmationListener
class ExpandRetryConfirmationListener implements Runnable
{
@Nullable
private final Utils.Proc<Boolean> mDialogClickListener;
ExpandRetryConfirmationListener(@NonNull Application app,
@Nullable Utils.Proc<Boolean> dialogClickListener)
ExpandRetryConfirmationListener(@Nullable Utils.Proc<Boolean> dialogClickListener)
{
super(app);
mDialogClickListener = dialogClickListener;
}
@Override
public void run()
{
super.run();
if (mDialogClickListener == null)
return;
mDialogClickListener.invoke(true);

View file

@ -106,8 +106,8 @@ public final class MapManager
})
.setPositiveButton(R.string.downloader_retry, (dialog, which) -> {
Application app = activity.getApplication();
RetryFailedDownloadConfirmationListener listener
= new ExpandRetryConfirmationListener(app, dialogClickListener);
ExpandRetryConfirmationListener listener
= new ExpandRetryConfirmationListener(dialogClickListener);
warn3gAndRetry(activity, errorData.countryId, listener);
}).create();
dlg.setCanceledOnTouchOutside(false);
@ -208,7 +208,7 @@ public final class MapManager
return warnOn3g(activity, countryId, () -> {
if (onAcceptListener != null)
onAcceptListener.run();
nativeDownload(countryId);
startDownload(countryId);
});
}
@ -217,10 +217,45 @@ public final class MapManager
return warnOn3g(activity, countryId, () -> {
if (onAcceptListener != null)
onAcceptListener.run();
nativeRetry(countryId);
retryDownload(countryId);
});
}
/**
* Enqueues failed items under given {@code root} node in downloader.
*/
public static void retryDownload(@NonNull String countryId) {
DownloaderService.startForegroundService();
nativeRetry(countryId);
}
/**
* Enqueues given {@code root} node with its children in downloader.
*/
public static void startUpdate(@NonNull String root) {
DownloaderService.startForegroundService();
nativeUpdate(root);
}
/**
* Enqueues the given list of nodes and its children in downloader.
*/
public static void startDownload(String... countries) {
DownloaderService.startForegroundService();
for (var countryId : countries)
{
nativeDownload(countryId);
}
}
/**
* Enqueues given {@code root} node and its children in downloader.
*/
public static void startDownload(@NonNull String countryId) {
DownloaderService.startForegroundService();
nativeDownload(countryId);
}
/**
* Retrieves ID of root node.
*/
@ -314,17 +349,17 @@ public final class MapManager
/**
* Enqueues given {@code root} node and its children in downloader.
*/
public static native void nativeDownload(String root);
private static native void nativeDownload(String root);
/**
* Enqueues failed items under given {@code root} node in downloader.
*/
public static native void nativeRetry(String root);
private static native void nativeRetry(String root);
/**
* Enqueues given {@code root} node with its children in downloader.
*/
public static native void nativeUpdate(String root);
private static native void nativeUpdate(String root);
/**
* Removes given currently downloading {@code root} node and its children from downloader.

View file

@ -140,8 +140,7 @@ public class OnmapDownloader implements MwmActivity.LeftAnimationTrackListener
{
mProgress.setPending(false);
mProgress.setProgress(Math.round(mCurrentCountry.progress));
sizeText = StringUtils.formatUsingSystemLocale("%1$s %2$.2f%%",
mActivity.getString(R.string.downloader_downloading), mCurrentCountry.progress);
sizeText = mActivity.getString(R.string.downloader_downloading) + " " + StringUtils.formatPercent(mCurrentCountry.progress / 100);
}
else
{
@ -167,7 +166,7 @@ public class OnmapDownloader implements MwmActivity.LeftAnimationTrackListener
if (TextUtils.equals(mCurrentCountry.id, country) &&
MapManager.nativeHasSpaceToDownloadCountry(country))
{
MapManager.nativeDownload(mCurrentCountry.id);
MapManager.startDownload(mCurrentCountry.id);
}
}
}
@ -211,12 +210,11 @@ public class OnmapDownloader implements MwmActivity.LeftAnimationTrackListener
boolean retry = (mCurrentCountry.status == CountryItem.STATUS_FAILED);
if (retry)
{
DownloaderNotifier.cancelNotification(mActivity.getApplicationContext());
MapManager.nativeRetry(mCurrentCountry.id);
MapManager.retryDownload(mCurrentCountry.id);
}
else
{
MapManager.nativeDownload(mCurrentCountry.id);
MapManager.startDownload(mCurrentCountry.id);
mActivity.requestPostNotificationsPermission();
}
}));

View file

@ -1,22 +0,0 @@
package app.organicmaps.downloader;
import android.app.Application;
import androidx.annotation.NonNull;
public class RetryFailedDownloadConfirmationListener implements Runnable
{
@NonNull
private final Application mApplication;
RetryFailedDownloadConfirmationListener(@NonNull Application application)
{
mApplication = application;
}
@Override
public void run()
{
DownloaderNotifier.cancelNotification(mApplication);
}
}

View file

@ -50,6 +50,8 @@ public class EditorFragment extends BaseMwmFragment implements View.OnClickListe
private View mCardName;
private View mCardAddress;
private View mCardDetails;
private View mCardSocialMedia;
private View mCardBuilding;
private RecyclerView mNamesView;
@ -93,7 +95,6 @@ public class EditorFragment extends BaseMwmFragment implements View.OnClickListe
private TextView mStreet;
private TextInputEditText mHouseNumber;
private View mBlockLevels;
private TextInputEditText mBuildingLevels;
// Define Metadata entries, that have more tricky logic, separately.
@ -135,6 +136,7 @@ public class EditorFragment extends BaseMwmFragment implements View.OnClickListe
private View mEditOpeningHours;
private TextInputEditText mDescription;
private final Map<Metadata.MetadataType, View> mDetailsBlocks = new HashMap<>();
private final Map<Metadata.MetadataType, View> mSocialMediaBlocks = new HashMap<>();
private TextView mReset;
private EditorHostFragment mParent;
@ -300,29 +302,29 @@ public class EditorFragment extends BaseMwmFragment implements View.OnClickListe
{
UiUtils.showIf(Editor.nativeIsNameEditable(), mCardName);
UiUtils.showIf(Editor.nativeIsAddressEditable(), mCardAddress);
UiUtils.showIf(Editor.nativeIsBuilding() && !Editor.nativeIsPointType(), mBlockLevels);
UiUtils.showIf(Editor.nativeIsBuilding() && !Editor.nativeIsPointType(), mCardBuilding);
final int[] editableDetails = Editor.nativeGetEditableProperties();
if (editableDetails.length == 0)
{
UiUtils.hide(mCardDetails);
return;
}
for (var e : mDetailsBlocks.entrySet())
setCardVisibility(mCardDetails, mDetailsBlocks, editableDetails);
setCardVisibility(mCardSocialMedia, mSocialMediaBlocks, editableDetails);
}
private void setCardVisibility(View card, Map<Metadata. MetadataType, View> blocks, int[] editableDetails) {
for (var e : blocks.entrySet())
UiUtils.hide(e.getValue());
boolean anyEditableDetails = false;
boolean anyBlockElement = false;
for (int type : editableDetails)
{
final View detailsBlock = mDetailsBlocks.get(Metadata.MetadataType.fromInt(type));
if (detailsBlock == null)
final View blockElement = blocks.get(Metadata.MetadataType.fromInt(type));
if (blockElement == null)
continue;
anyEditableDetails = true;
UiUtils.show(detailsBlock);
anyBlockElement = true;
UiUtils.show(blockElement);
}
UiUtils.showIf(anyEditableDetails, mCardDetails);
UiUtils.showIf(anyBlockElement, card);
}
private void refreshOpeningTime()
@ -412,20 +414,22 @@ public class EditorFragment extends BaseMwmFragment implements View.OnClickListe
mCardName = view.findViewById(R.id.cv__name);
mCardAddress = view.findViewById(R.id.cv__address);
mCardDetails = view.findViewById(R.id.cv__details);
mCardSocialMedia = view.findViewById(R.id.cv__social_media);
mCardBuilding = view.findViewById(R.id.cv__building);
initNamesView(view);
// Address
view.findViewById(R.id.block_street).setOnClickListener(this);
mStreet = view.findViewById(R.id.street);
View blockHouseNumber = view.findViewById(R.id.block_building);
mHouseNumber = findInputAndInitBlock(blockHouseNumber, 0, R.string.house_number);
mHouseNumber = findInputAndInitBlock(blockHouseNumber, R.drawable.ic_building, R.string.house_number);
mInputHouseNumber = blockHouseNumber.findViewById(R.id.custom_input);
initBlock(view, Metadata.MetadataType.FMD_POSTCODE, R.id.block_zipcode, 0, R.string.editor_zip_code, 0);
initBlock(view, Metadata.MetadataType.FMD_POSTCODE, R.id.block_zipcode, R.drawable.ic_address, R.string.editor_zip_code, 0);
// Details
mBlockLevels = view.findViewById(R.id.block_levels);
mBuildingLevels = findInputAndInitBlock(mBlockLevels, 0,
View mBlockLevels = view.findViewById(R.id.block_levels);
mBuildingLevels = findInputAndInitBlock(mBlockLevels, R.drawable.ic_floor,
getString(R.string.editor_storey_number, Editor.nativeGetMaxEditableBuildingLevels()));
mBuildingLevels.setInputType(InputType.TYPE_CLASS_NUMBER);
mInputBuildingLevels = mBlockLevels.findViewById(R.id.custom_input);
@ -493,12 +497,13 @@ public class EditorFragment extends BaseMwmFragment implements View.OnClickListe
mDetailsBlocks.put(Metadata.MetadataType.FMD_WEBSITE, websiteBlock);
mDetailsBlocks.put(Metadata.MetadataType.FMD_WEBSITE_MENU, websiteMenuBlock);
mDetailsBlocks.put(Metadata.MetadataType.FMD_EMAIL, emailBlock);
mDetailsBlocks.put(Metadata.MetadataType.FMD_CONTACT_FACEBOOK, facebookContactBlock);
mDetailsBlocks.put(Metadata.MetadataType.FMD_CONTACT_INSTAGRAM, instagramContactBlock);
mDetailsBlocks.put(Metadata.MetadataType.FMD_CONTACT_TWITTER, twitterContactBlock);
mDetailsBlocks.put(Metadata.MetadataType.FMD_CONTACT_VK, vkContactBlock);
mDetailsBlocks.put(Metadata.MetadataType.FMD_CONTACT_LINE, lineContactBlock);
mDetailsBlocks.put(Metadata.MetadataType.FMD_OPERATOR, operatorBlock);
mSocialMediaBlocks.put(Metadata.MetadataType.FMD_CONTACT_FACEBOOK, facebookContactBlock);
mSocialMediaBlocks.put(Metadata.MetadataType.FMD_CONTACT_INSTAGRAM, instagramContactBlock);
mSocialMediaBlocks.put(Metadata.MetadataType.FMD_CONTACT_TWITTER, twitterContactBlock);
mSocialMediaBlocks.put(Metadata.MetadataType.FMD_CONTACT_VK, vkContactBlock);
mSocialMediaBlocks.put(Metadata.MetadataType.FMD_CONTACT_LINE, lineContactBlock);
}
private static TextInputEditText findInput(View blockWithInput)

View file

@ -350,7 +350,7 @@ public class EditorHostFragment extends BaseMwmToolbarFragment implements View.O
private void processEditedFeatures()
{
Context context = requireContext();
if (OsmOAuth.isAuthorized(context) || !ConnectionState.INSTANCE.isConnected())
if (OsmOAuth.isAuthorized(context))
{
Utils.navigateToParent(requireActivity());
return;

View file

@ -3,7 +3,6 @@ package app.organicmaps.intent;
import android.content.ContentResolver;
import android.content.Intent;
import android.net.Uri;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.core.content.IntentCompat;
@ -22,7 +21,7 @@ import app.organicmaps.bookmarks.data.MapObject;
import app.organicmaps.editor.OsmLoginActivity;
import app.organicmaps.routing.RoutingController;
import app.organicmaps.search.SearchActivity;
import app.organicmaps.search.SearchEngine;
import app.organicmaps.sdk.search.SearchEngine;
import app.organicmaps.util.StorageUtils;
import app.organicmaps.util.concurrency.ThreadPool;
@ -99,7 +98,7 @@ public class Factory
RoutingController.get().prepare(MapObject.createMapObject(FeatureId.EMPTY, MapObject.API_POINT,
from.mName, "", from.mLat, from.mLon),
MapObject.createMapObject(FeatureId.EMPTY, MapObject.API_POINT,
to.mName, "", to.mLat, to.mLon), true);
to.mName, "", to.mLat, to.mLon));
return true;
case RequestType.SEARCH:
{

View file

@ -200,6 +200,8 @@ public class TrackRecordingService extends Service implements LocationListener
.setSmallIcon(R.drawable.warning_icon)
.setContentTitle(context.getString(R.string.current_location_unknown_error_title))
.setContentText(context.getString(R.string.dialog_routing_location_turn_wifi))
.setStyle(new NotificationCompat.BigTextStyle()
.bigText(context.getString(R.string.dialog_routing_location_turn_wifi)))
.addAction(0, context.getString(R.string.navigation_stop_button), getExitPendingIntent(context))
.setContentIntent(getPendingIntent(context))
.setColor(ContextCompat.getColor(context, R.color.notification_warning));

View file

@ -1,6 +1,9 @@
package app.organicmaps.maplayer;
import android.animation.ArgbEvaluator;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.TypedValue;
@ -52,6 +55,8 @@ public class MapButtonsController extends Fragment
private View mBottomButtonsFrame;
@Nullable
private LayersButton mToggleMapLayerButton;
@Nullable
FloatingActionButton mTrackRecordingStatusButton;
@Nullable
private MyPositionButton mNavMyPosition;
@ -68,7 +73,11 @@ public class MapButtonsController extends Fragment
private final Observer<Boolean> mButtonHiddenObserver = this::setButtonsHidden;
private final Observer<Integer> mMyPositionModeObserver = this::updateNavMyPositionButton;
private final Observer<SearchWheel.SearchOption> mSearchOptionObserver = this::onSearchOptionChange;
private final Observer<Boolean> mTrackRecorderObserver = this::updateMenuBadge;
private final Observer<Boolean> mTrackRecorderObserver = (enable) -> {
updateMenuBadge(enable);
showButton(enable, MapButtons.trackRecordingStatus);
};
private final Observer<Integer> mTopButtonMarginObserver = this::updateTopButtonsMargin;
@Nullable
@Override
@ -119,6 +128,10 @@ public class MapButtonsController extends Fragment
mToggleMapLayerButton.setOnClickListener(view -> mMapButtonClickListener.onMapButtonClick(MapButtons.toggleMapLayer));
mToggleMapLayerButton.setVisibility(View.VISIBLE);
}
mMapButtonsViewModel.setTopButtonsMarginTop(-1);
mTrackRecordingStatusButton = mFrame.findViewById(R.id.track_recording_status);
if (mTrackRecordingStatusButton != null)
mTrackRecordingStatusButton.setOnClickListener(view -> mMapButtonClickListener.onMapButtonClick(MapButtons.trackRecordingStatus));
final View menuButton = mFrame.findViewById(R.id.menu_button);
if (menuButton != null)
{
@ -157,6 +170,9 @@ public class MapButtonsController extends Fragment
mButtonsMap.put(MapButtons.menu, menuButton);
if (helpButton != null)
mButtonsMap.put(MapButtons.help, helpButton);
if (mTrackRecordingStatusButton != null)
mButtonsMap.put(MapButtons.trackRecordingStatus, mTrackRecordingStatusButton);
showButton(false, MapButtons.trackRecordingStatus);
return mFrame;
}
@ -184,6 +200,28 @@ public class MapButtonsController extends Fragment
case bookmarks:
case menu:
UiUtils.showIf(show, buttonView);
break;
case trackRecordingStatus:
UiUtils.showIf(show, buttonView);
animateIconBlinking(show, (FloatingActionButton) buttonView);
}
}
void animateIconBlinking(boolean show, @NonNull FloatingActionButton button)
{
if (show)
{
Drawable drawable = button.getDrawable();
ObjectAnimator colorAnimator = ObjectAnimator.ofArgb(
drawable,
"tint",
0xFF757575,
0xFFFF0000);
colorAnimator.setDuration(2500);
colorAnimator.setEvaluator(new ArgbEvaluator());
colorAnimator.setRepeatCount(ObjectAnimator.INFINITE);
colorAnimator.setRepeatMode(ObjectAnimator.REVERSE);
colorAnimator.start();
}
}
@ -192,6 +230,15 @@ public class MapButtonsController extends Fragment
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, context.getResources().getDisplayMetrics());
}
private void updateTopButtonsMargin(int margin)
{
if (margin == -1 || mTrackRecordingStatusButton == null)
return;
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) mTrackRecordingStatusButton.getLayoutParams();
params.topMargin = margin;
mTrackRecordingStatusButton.setLayoutParams(params);
}
@OptIn(markerClass = ExperimentalBadgeUtils.class)
private void updateMenuBadge(Boolean enable)
{
@ -352,6 +399,7 @@ public class MapButtonsController extends Fragment
mMapButtonsViewModel.getMyPositionMode().observe(activity, mMyPositionModeObserver);
mMapButtonsViewModel.getSearchOption().observe(activity, mSearchOptionObserver);
mMapButtonsViewModel.getTrackRecorderState().observe(activity, mTrackRecorderObserver);
mMapButtonsViewModel.getTopButtonsMarginTop().observe(activity, mTopButtonMarginObserver);
}
public void onResume()
@ -378,6 +426,7 @@ public class MapButtonsController extends Fragment
public void onStop()
{
super.onStop();
mMapButtonsViewModel.getTopButtonsMarginTop().removeObserver(mTopButtonMarginObserver);
mPlacePageViewModel.getPlacePageDistanceToTop().removeObserver(mPlacePageDistanceToTopObserver);
mMapButtonsViewModel.getButtonsHidden().removeObserver(mButtonHiddenObserver);
mMapButtonsViewModel.getMyPositionMode().removeObserver(mMyPositionModeObserver);
@ -407,7 +456,8 @@ public class MapButtonsController extends Fragment
search,
bookmarks,
menu,
help
help,
trackRecordingStatus
}
public interface MapButtonClickListener

View file

@ -9,6 +9,7 @@ public class MapButtonsViewModel extends ViewModel
{
private final MutableLiveData<Boolean> mButtonsHidden = new MutableLiveData<>(false);
private final MutableLiveData<Float> mBottomButtonsHeight = new MutableLiveData<>(0f);
private final MutableLiveData<Integer> mTopButtonsMarginTop = new MutableLiveData<>(-1);
private final MutableLiveData<MapButtonsController.LayoutMode> mLayoutMode = new MutableLiveData<>(MapButtonsController.LayoutMode.regular);
private final MutableLiveData<Integer> mMyPositionMode = new MutableLiveData<>();
private final MutableLiveData<SearchWheel.SearchOption> mSearchOption = new MutableLiveData<>();
@ -34,6 +35,16 @@ public class MapButtonsViewModel extends ViewModel
mBottomButtonsHeight.setValue(height);
}
public MutableLiveData<Integer> getTopButtonsMarginTop()
{
return mTopButtonsMarginTop;
}
public void setTopButtonsMarginTop(int margin)
{
mTopButtonsMarginTop.setValue(margin);
}
public MutableLiveData<MapButtonsController.LayoutMode> getLayoutMode()
{
return mLayoutMode;

View file

@ -16,7 +16,7 @@ import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import app.organicmaps.R;
import app.organicmaps.routing.RoutingController;
import app.organicmaps.search.SearchEngine;
import app.organicmaps.sdk.search.SearchEngine;
import app.organicmaps.util.Graphics;
import app.organicmaps.util.UiUtils;
import app.organicmaps.util.concurrency.UiThread;

View file

@ -0,0 +1,265 @@
package app.organicmaps.routing;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.RectF;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.appcompat.content.res.AppCompatResources;
import androidx.recyclerview.widget.RecyclerView;
import app.organicmaps.R;
import app.organicmaps.bookmarks.data.MapObject;
import app.organicmaps.util.StringUtils;
import app.organicmaps.util.UiUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
public class ManageRouteAdapter extends RecyclerView.Adapter<ManageRouteAdapter.ManageRouteViewHolder>
{
Context mContext;
ArrayList<RouteMarkData> mRoutePoints;
ManageRouteListener mManageRouteListener;
public interface ManageRouteListener
{
void startDrag(RecyclerView.ViewHolder viewHolder);
void showMyLocationIcon(boolean showMyLocationIcon);
void onRoutePointDeleted(RecyclerView.ViewHolder viewHolder);
}
public ManageRouteAdapter(Context context, RouteMarkData[] routeMarkData, ManageRouteListener listener)
{
mContext = context;
mRoutePoints = new ArrayList<>(Arrays.asList(routeMarkData));
mManageRouteListener = listener;
updateMyLocationIcon();
}
@NonNull
@Override
public ManageRouteViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType)
{
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.manage_route_list_item,
parent, false);
return new ManageRouteViewHolder(view);
}
@SuppressLint("ClickableViewAccessibility")
@Override
public void onBindViewHolder(@NonNull ManageRouteViewHolder holder, int position)
{
// Set route point icon.
int iconId;
switch (mRoutePoints.get(position).mPointType)
{
case RoutePointInfo.ROUTE_MARK_START: // Starting point.
if (mRoutePoints.get(position).mIsMyPosition)
iconId = R.drawable.ic_location_arrow_blue;
else
iconId = R.drawable.route_point_start;
break;
case RoutePointInfo.ROUTE_MARK_INTERMEDIATE: // Intermediate stop.
TypedArray iconArray = mContext.getResources().obtainTypedArray(R.array.route_stop_icons);
iconId = iconArray.getResourceId(mRoutePoints.get(position).mIntermediateIndex,
R.drawable.route_point_20);
iconArray.recycle();
break;
case RoutePointInfo.ROUTE_MARK_FINISH: // Destination point.
iconId = R.drawable.route_point_finish;
break;
default: // Unknown route type.
iconId = R.drawable.warning_icon;
break;
}
// Set icon widget.
holder.mImageViewIcon.setImageDrawable(AppCompatResources.getDrawable(mContext, iconId));
// Set title & subtitle.
String title, subtitle;
if (mRoutePoints.get(position).mIsMyPosition)
{
// My position point.
title = mContext.getString(R.string.core_my_position);
if (mRoutePoints.get(position).mPointType != RoutePointInfo.ROUTE_MARK_START)
subtitle = mRoutePoints.get(position).mTitle;
else
{
// Hide my position coordinates if it's the starting point of the route.
subtitle = "";
}
}
else
{
title = mRoutePoints.get(position).mTitle;
subtitle = mRoutePoints.get(position).mSubtitle;
}
holder.mTextViewTitle.setText(title);
holder.mTextViewSubtitle.setText(subtitle);
UiUtils.showIf(subtitle != null && !subtitle.isEmpty(), holder.mTextViewSubtitle);
// Show 'Delete' icon button only if we have intermediate stops.
UiUtils.showIf(mRoutePoints.size() > 2, holder.mImageViewDelete);
// Detection of touch events on holder view.
holder.mItemView.setOnTouchListener((v, event) -> {
if (event.getAction() == MotionEvent.ACTION_DOWN)
{
RectF deleteButtonRect = new RectF(holder.mImageViewDelete.getLeft(),
holder.mImageViewDelete.getTop(),
holder.mImageViewDelete.getRight(),
holder.mImageViewDelete.getBottom());
if (holder.mImageViewDelete.isShown() && deleteButtonRect.contains(event.getX(), event.getY()))
{
// User has clicked on the 'Delete' icon button.
mManageRouteListener.onRoutePointDeleted(holder);
}
else
{
// Call start drag listener on touch.
mManageRouteListener.startDrag(holder);
}
}
return false;
});
}
@Override
public int getItemCount()
{
return mRoutePoints.size();
}
public void moveRoutePoint(int draggedItemIndex, int targetIndex)
{
if (draggedItemIndex == targetIndex) // Dragged to same spot. Do nothing.
return;
Collections.swap(mRoutePoints, draggedItemIndex, targetIndex);
updateRoutePointsData();
notifyItemMoved(draggedItemIndex, targetIndex);
}
public void deleteRoutePoint(RecyclerView.ViewHolder viewHolder)
{
mRoutePoints.remove(viewHolder.getAbsoluteAdapterPosition());
updateRoutePointsData();
notifyItemRemoved(viewHolder.getAbsoluteAdapterPosition());
}
public void setMyLocationAsStartingPoint(MapObject myLocation)
{
String latLonString = StringUtils.formatUsingUsLocale("%.6f, %.6f",
myLocation.getLat(),
myLocation.getLon());
// Replace route point in first position with 'My Position".
mRoutePoints.set(0, new RouteMarkData(latLonString, "", RoutePointInfo.ROUTE_MARK_START,
0, true, true, false, myLocation.getLat(),
myLocation.getLon()));
// Update data.
updateRoutePointsData();
// Update adapter.
notifyItemChanged(0);
// Show 'My location' crosshair button.
if (mManageRouteListener != null)
mManageRouteListener.showMyLocationIcon(true);
}
private void updateMyLocationIcon()
{
boolean containsMyLocationPoint = false;
for (RouteMarkData routePoint : mRoutePoints)
{
if (routePoint.mIsMyPosition)
{
containsMyLocationPoint = true;
break;
}
}
if (mManageRouteListener != null)
mManageRouteListener.showMyLocationIcon(!containsMyLocationPoint);
}
private void updateRoutePointsData()
{
assert(mRoutePoints.size() >= 2);
// Set starting point.
mRoutePoints.get(0).mPointType = RoutePointInfo.ROUTE_MARK_START;
// Set finish point.
mRoutePoints.get(mRoutePoints.size() - 1).mPointType = RoutePointInfo.ROUTE_MARK_FINISH;
// Set intermediate point(s).
for (int pos = 1; pos < mRoutePoints.size() - 1; pos++)
{
mRoutePoints.get(pos).mPointType = RoutePointInfo.ROUTE_MARK_INTERMEDIATE;
mRoutePoints.get(pos).mIntermediateIndex = pos - 1;
}
}
public ArrayList<RouteMarkData> getRoutePoints()
{
return mRoutePoints;
}
static class ManageRouteViewHolder extends RecyclerView.ViewHolder
{
@NonNull
public final View mItemView;
@NonNull
public final ImageView mImageViewIcon;
@NonNull
public final TextView mTextViewTitle;
@NonNull
public final TextView mTextViewSubtitle;
@NonNull
public final ImageView mImageViewDelete;
ManageRouteViewHolder(@NonNull View itemView)
{
super(itemView);
mItemView = itemView;
mImageViewIcon = itemView.findViewById(R.id.type_icon);
mTextViewTitle = itemView.findViewById(R.id.title);
mTextViewSubtitle = itemView.findViewById(R.id.subtitle);
mImageViewDelete = itemView.findViewById(R.id.delete_icon);
}
}
}

View file

@ -0,0 +1,266 @@
package app.organicmaps.routing;
import android.app.Dialog;
import android.content.res.Resources;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.ImageView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import app.organicmaps.Framework;
import app.organicmaps.MwmApplication;
import app.organicmaps.R;
import app.organicmaps.bookmarks.data.MapObject;
import app.organicmaps.util.UiUtils;
import com.google.android.material.bottomsheet.BottomSheetBehavior;
import com.google.android.material.bottomsheet.BottomSheetDialog;
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
import com.google.android.material.divider.MaterialDividerItemDecoration;
import java.util.ArrayList;
import static androidx.recyclerview.widget.ItemTouchHelper.*;
public class ManageRouteBottomSheet extends BottomSheetDialogFragment
implements View.OnClickListener, ManageRouteAdapter.ManageRouteListener
{
ManageRouteAdapter mManageRouteAdapter;
ItemTouchHelper mTouchHelper;
ImageView mMyLocationImageView;
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState)
{
View v = inflater.inflate(R.layout.manage_route_bottom_sheet, container, false);
Button cancelButton = v.findViewById(R.id.btn__cancel);
cancelButton.setOnClickListener(this);
Button planButton = v.findViewById(R.id.btn__plan);
planButton.setOnClickListener(this);
mMyLocationImageView = v.findViewById(R.id.image_my_location);
mMyLocationImageView.setOnClickListener(this);
RecyclerView manageRouteList = v.findViewById(R.id.manage_route_list);
LinearLayoutManager layoutManager = new LinearLayoutManager(getContext());
manageRouteList.setLayoutManager(layoutManager);
RecyclerView.ItemDecoration decoration = new MaterialDividerItemDecoration(getContext(),
layoutManager.getOrientation());
manageRouteList.addItemDecoration(decoration);
mManageRouteAdapter = new ManageRouteAdapter(getContext(), Framework.nativeGetRoutePoints(), this);
manageRouteList.setAdapter(mManageRouteAdapter);
// Enable drag & drop in route list.
mTouchHelper = new ItemTouchHelper(new ManageRouteItemTouchHelperCallback(mManageRouteAdapter,
getResources()));
mTouchHelper.attachToRecyclerView(manageRouteList);
return v;
}
@NonNull
@Override
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState)
{
Dialog dialog = super.onCreateDialog(savedInstanceState);
// Expand bottom sheet dialog.
dialog.setOnShowListener(dialogInterface -> {
FrameLayout bottomSheet = ((BottomSheetDialog) dialogInterface).findViewById(
com.google.android.material.R.id.design_bottom_sheet);
if (bottomSheet != null)
BottomSheetBehavior.from(bottomSheet).setState(BottomSheetBehavior.STATE_EXPANDED);
});
// Set key listener to detect back button pressed.
dialog.setOnKeyListener((dialog1, keyCode, event) -> {
if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP)
{
// Dismiss the fragment
dismiss();
return true;
}
// Otherwise, do nothing else.
return false;
});
return dialog;
}
@Override
public void onClick(View v)
{
int buttonId = v.getId();
if (buttonId == R.id.btn__cancel)
{
// Close dialog if 'Cancel' button is pressed.
dismiss();
}
else if (buttonId == R.id.image_my_location)
{
// Get current location.
MapObject myLocation = MwmApplication.from(getContext()).getLocationHelper().getMyPosition();
// Set 'My Location' as starting point of the route.
if (myLocation != null)
mManageRouteAdapter.setMyLocationAsStartingPoint(myLocation);
}
else if (buttonId == R.id.btn__plan)
{
// Get route points from adapter.
ArrayList<RouteMarkData> newRoutePoints = mManageRouteAdapter.getRoutePoints();
// Make sure that the new route contains at least 2 points (start and destination).
assert(newRoutePoints.size() >= 2);
// Remove all existing route points.
Framework.nativeRemoveRoutePoints();
// First, add the destination point.
Framework.addRoutePoint(newRoutePoints.get(newRoutePoints.size() - 1));
// Secondly, add the starting point.
Framework.addRoutePoint(newRoutePoints.get(0));
// And then, add all intermediate points.
for (int pos = 1; pos < newRoutePoints.size() - 1; pos++)
Framework.addRoutePoint(newRoutePoints.get(pos));
// Intermediate route points are added sorted by distance.
// We have to make sure that they follow the requested order.
RouteMarkData[] finalRoutePoints = Framework.nativeGetRoutePoints();
for (int first = 1; first < newRoutePoints.size() - 1; first++)
{
int secondIndex = -1;
for (int second = first; second < newRoutePoints.size() - 1; second++)
{
if (finalRoutePoints[first].equals(newRoutePoints.get(second)))
{
secondIndex = second;
break;
}
}
if (secondIndex < 0)
{
// Something went bad. Intermediate point not found in the route points.
break;
}
if (first != secondIndex)
{
// Intermediate point needs to be moved.
Framework.nativeMoveRoutePoint(secondIndex, first);
// Refresh final route points.
finalRoutePoints = Framework.nativeGetRoutePoints();
}
}
// Launch route planning.
RoutingController.get().launchPlanning();
// Dismiss (close) manage route bottom sheet.
dismiss();
}
}
@Override
public void startDrag(RecyclerView.ViewHolder viewHolder)
{
// Start dragging.
mTouchHelper.startDrag(viewHolder);
}
@Override
public void showMyLocationIcon(boolean showMyLocationIcon)
{
// Get current location.
MapObject myLocation = MwmApplication.from(getContext()).getLocationHelper().getMyPosition();
UiUtils.showIf(showMyLocationIcon && myLocation != null, mMyLocationImageView);
}
@Override
public void onRoutePointDeleted(RecyclerView.ViewHolder viewHolder)
{
mManageRouteAdapter.deleteRoutePoint(viewHolder);
mManageRouteAdapter.notifyDataSetChanged();
}
private static class ManageRouteItemTouchHelperCallback extends ItemTouchHelper.Callback
{
private final ManageRouteAdapter mManageRouteAdapter;
public ManageRouteItemTouchHelperCallback(ManageRouteAdapter adapter, Resources resources)
{
mManageRouteAdapter = adapter;
}
@Override
public int getMovementFlags(@NonNull RecyclerView recyclerView,
@NonNull RecyclerView.ViewHolder viewHolder)
{
// Enable up & down dragging. No left-right swiping is enabled.
return makeMovementFlags(UP | DOWN, 0);
}
@Override
public boolean isLongPressDragEnabled()
{
return false;
}
@Override
public boolean isItemViewSwipeEnabled()
{
return false;
}
@Override
public boolean onMove(@NonNull RecyclerView recyclerView,
@NonNull RecyclerView.ViewHolder viewHolder,
@NonNull RecyclerView.ViewHolder target)
{
mManageRouteAdapter.moveRoutePoint(viewHolder.getAbsoluteAdapterPosition(),
target.getAbsoluteAdapterPosition());
return true;
}
@Override
public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction)
{
}
@Override
public void clearView(@NonNull RecyclerView recyclerView,
@NonNull RecyclerView.ViewHolder viewHolder)
{
super.clearView(recyclerView, viewHolder);
// Called when dragging action has finished.
mManageRouteAdapter.notifyDataSetChanged();
}
}
}

View file

@ -12,15 +12,18 @@ import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import androidx.lifecycle.ViewModelProvider;
import app.organicmaps.Framework;
import app.organicmaps.R;
import app.organicmaps.location.LocationHelper;
import app.organicmaps.maplayer.MapButtonsViewModel;
import app.organicmaps.maplayer.traffic.TrafficManager;
import app.organicmaps.util.StringUtils;
import app.organicmaps.util.UiUtils;
import app.organicmaps.util.Utils;
import app.organicmaps.util.WindowInsetUtils;
import app.organicmaps.widget.LanesView;
import app.organicmaps.widget.SpeedLimitView;
import app.organicmaps.util.WindowInsetUtils;
import app.organicmaps.widget.menu.NavMenu;
import com.google.android.material.bottomsheet.BottomSheetBehavior;
@ -44,6 +47,8 @@ public class NavigationController implements TrafficManager.TrafficCallback,
@NonNull
private final SpeedLimitView mSpeedLimit;
private final MapButtonsViewModel mMapButtonsViewModel;
private final NavMenu mNavMenu;
View.OnClickListener mOnSettingsClickListener;
@ -59,6 +64,8 @@ public class NavigationController implements TrafficManager.TrafficCallback,
public NavigationController(AppCompatActivity activity, View.OnClickListener onSettingsClickListener,
NavMenu.OnMenuSizeChangedListener onMenuSizeChangedListener)
{
mMapButtonsViewModel = new ViewModelProvider(activity).get(MapButtonsViewModel.class);
mFrame = activity.findViewById(R.id.navigation_frame);
mNavMenu = new NavMenu(activity, this, onMenuSizeChangedListener);
mOnSettingsClickListener = onSettingsClickListener;
@ -156,6 +163,10 @@ public class NavigationController implements TrafficManager.TrafficCallback,
UiUtils.visibleIf(hasStreet, mStreetFrame);
if (!TextUtils.isEmpty(info.nextStreet))
mNextStreet.setText(info.nextStreet);
int margin = UiUtils.dimen(mFrame.getContext(), R.dimen.nav_frame_padding);
if (hasStreet)
margin += mStreetFrame.getHeight();
mMapButtonsViewModel.setTopButtonsMarginTop(margin);
}
public void show(boolean show)
@ -249,10 +260,10 @@ public class NavigationController implements TrafficManager.TrafficCallback,
{
final Location location = LocationHelper.from(mFrame.getContext()).getSavedLocation();
if (location == null) {
mSpeedLimit.setSpeedLimitMps(0);
mSpeedLimit.setSpeedLimit(0, false);
return;
}
mSpeedLimit.setCurrentSpeed(location.getSpeed());
mSpeedLimit.setSpeedLimitMps(info.speedLimitMps);
final boolean speedLimitExceeded = info.speedLimitMps < location.getSpeed();
mSpeedLimit.setSpeedLimit(StringUtils.nativeFormatSpeed(info.speedLimitMps), speedLimitExceeded);
}
}

View file

@ -16,8 +16,8 @@ public class RouteMarkData
@Nullable
public final String mSubtitle;
@RoutePointInfo.RouteMarkType
public final int mPointType;
public final int mIntermediateIndex;
public int mPointType;
public int mIntermediateIndex;
public final boolean mIsVisible;
public final boolean mIsMyPosition;
public final boolean mIsPassed;
@ -39,4 +39,12 @@ public class RouteMarkData
mLat = lat;
mLon = lon;
}
public boolean equals(RouteMarkData other)
{
return mTitle != null && other.mTitle != null &&
mTitle.compareTo(other.mTitle) == 0 &&
mSubtitle != null && other.mSubtitle != null &&
mSubtitle.compareTo(other.mSubtitle) == 0;
}
}

View file

@ -151,6 +151,8 @@ final class RoutingBottomMenuController implements View.OnClickListener
Resources res = mContext.getResources();
mTransitViewDecorator = new DotDividerItemDecoration(dividerDrawable, res.getDimensionPixelSize(R.dimen.margin_base),
res.getDimensionPixelSize(R.dimen.margin_half));
Button manageRouteButton = altitudeChartFrame.findViewById(R.id.btn__manage_route);
manageRouteButton.setOnClickListener(this);
}
void showAltitudeChartAndRoutingDetails()
@ -253,7 +255,7 @@ final class RoutingBottomMenuController implements View.OnClickListener
if (LocationHelper.from(mContext).getMyPosition() != null)
{
UiUtils.show(mActionButton);
Drawable icon = ContextCompat.getDrawable(mContext, R.drawable.ic_my_location);
Drawable icon = ContextCompat.getDrawable(mContext, R.drawable.ic_location_crosshair);
int colorAccent = ContextCompat.getColor(mContext,
UiUtils.getStyledResourceId(mContext, androidx.appcompat.R.attr.colorAccent));
mActionIcon.setImageDrawable(Graphics.tint(icon, colorAccent));
@ -485,5 +487,7 @@ final class RoutingBottomMenuController implements View.OnClickListener
int pointType = (Integer) mActionMessage.getTag();
mListener.onSearchRoutePoint(pointType);
}
else if (id == R.id.btn__manage_route && mListener != null)
mListener.onManageRouteOpen();
}
}

View file

@ -5,4 +5,5 @@ public interface RoutingBottomMenuListener
void onUseMyPositionAsStart();
void onSearchRoutePoint(@RoutePointInfo.RouteMarkType int type);
void onRoutingStart();
void onManageRouteOpen();
}

View file

@ -60,7 +60,7 @@ public class RoutingController
default void onResetToPlanningState() {}
default void onBuiltRoute() {}
default void onDrivingOptionsWarning() {}
default boolean isSubwayEnabled() { return false; }
default void onCommonBuildError(int lastResultCode, @NonNull String[] lastMissingMaps) {}
default void onDrivingOptionsBuildError() {}
@ -333,45 +333,23 @@ public class RoutingController
{
setState(State.NONE);
setBuildState(BuildState.NONE);
prepare(getStartPoint(), getEndPoint(), false);
prepare(getStartPoint(), getEndPoint());
}
public void prepare(@Nullable MapObject startPoint, @Nullable MapObject endPoint)
{
prepare(startPoint, endPoint, false);
}
public void prepare(@Nullable MapObject startPoint, @Nullable MapObject endPoint, boolean fromApi)
{
Logger.d(TAG, "prepare (" + (endPoint == null ? "route)" : "p2p)"));
initLastRouteType(startPoint, endPoint, fromApi);
initLastRouteType(startPoint, endPoint);
prepare(startPoint, endPoint, mLastRouterType);
}
private void initLastRouteType(@Nullable MapObject startPoint, @Nullable MapObject endPoint,
boolean fromApi)
private void initLastRouteType(@Nullable MapObject startPoint, @Nullable MapObject endPoint)
{
if (shouldForceTransitRoute(fromApi))
{
mLastRouterType = Framework.ROUTER_TYPE_TRANSIT;
return;
}
if (startPoint != null && endPoint != null)
mLastRouterType = Framework.nativeGetBestRouter(startPoint.getLat(), startPoint.getLon(),
endPoint.getLat(), endPoint.getLon());
}
private boolean isSubwayEnabled()
{
return mContainer != null && mContainer.isSubwayEnabled();
}
private boolean shouldForceTransitRoute(boolean fromApi)
{
return mState == State.NONE && isSubwayEnabled() && !fromApi;
}
public void prepare(final @Nullable MapObject startPoint, final @Nullable MapObject endPoint,
@Framework.RouterType int routerType)
{
@ -426,6 +404,17 @@ public class RoutingController
resetToPlanningStateIfNavigating();
}
public void launchPlanning()
{
build();
setState(State.PREPARE);
startPlanning();
if (mContainer != null)
mContainer.updateMenu();
if (mContainer != null)
mContainer.onResetToPlanningState();
}
/**
* @return False if not navigating, true otherwise
*/

View file

@ -2,10 +2,13 @@ package app.organicmaps.routing;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Typeface;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Pair;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@ -35,7 +38,15 @@ public class RoutingErrorDialogFragment extends BaseRoutingErrorDialogFragment
ResultCodesHelper.getDialogTitleSubtitle(requireContext(), mResultCode, mMissingMaps.size());
Pair<String, String> titleMessage = resHolder.getTitleMessage();
builder.setTitle(titleMessage.first);
TextView titleView = new TextView(requireContext());
titleView.setText(titleMessage.first);
titleView.setPadding(65, 32, 32, 16);
titleView.setTextSize(18);
titleView.setMaxLines(4);
titleView.setEllipsize(TextUtils.TruncateAt.END);
titleView.setTypeface(null, Typeface.BOLD);
builder.setCustomTitle(titleView);
mMessage = titleMessage.second;
builder.setNegativeButton(resHolder.getCancelBtnResId(), null);
if (ResultCodesHelper.isDownloadable(mResultCode, mMissingMaps.size()))

View file

@ -42,8 +42,7 @@ public class RoutingMapsDownloadFragment extends BaseRoutingErrorDialogFragment
mMapsArray[i] = item.id;
}
for (String map : mMaps)
MapManager.nativeDownload(map);
MapManager.startDownload(mMapsArray);
}
private View setupFrame(View frame)

View file

@ -1,12 +1,14 @@
package app.organicmaps.routing;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.CompoundButton;
import android.widget.RadioGroup;
import android.widget.TextView;
import androidx.activity.result.ActivityResultLauncher;
import androidx.annotation.DrawableRes;
import androidx.annotation.IdRes;
import androidx.annotation.NonNull;
@ -78,6 +80,7 @@ public class RoutingPlanController extends ToolbarController
}
RoutingPlanController(View root, Activity activity,
ActivityResultLauncher<Intent> startDrivingOptionsForResult,
@NonNull RoutingPlanInplaceController.RoutingPlanListener routingPlanListener,
@Nullable RoutingBottomMenuListener listener)
{
@ -103,7 +106,7 @@ public class RoutingPlanController extends ToolbarController
View btn = mDrivingOptionsBtnContainer.findViewById(R.id.driving_options_btn);
mDrivingOptionsImage = mFrame.findViewById(R.id.driving_options_btn_img);
btn.setOnClickListener(v -> DrivingOptionsActivity.start(requireActivity()));
btn.setOnClickListener(v -> DrivingOptionsActivity.start(requireActivity(), startDrivingOptionsForResult));
mDriverOptionsLayoutListener = new SelfTerminatedDrivingOptionsLayoutListener();
mAnimToggle = MwmApplication.from(activity.getApplicationContext())
.getResources().getInteger(R.integer.anim_default);

View file

@ -7,9 +7,9 @@ import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.FragmentActivity;
import app.organicmaps.Framework;
import app.organicmaps.MwmActivity;
import app.organicmaps.R;
import app.organicmaps.base.BaseMwmFragment;
@ -21,15 +21,9 @@ public class RoutingPlanFragment extends BaseMwmFragment
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState)
{
final FragmentActivity activity = requireActivity();
final MwmActivity activity = (MwmActivity) requireActivity();
View res = inflater.inflate(R.layout.fragment_routing, container, false);
RoutingBottomMenuListener listener = null;
if (activity instanceof RoutingBottomMenuListener)
listener = (RoutingBottomMenuListener) activity;
RoutingPlanInplaceController.RoutingPlanListener planListener =
(RoutingPlanInplaceController.RoutingPlanListener) activity;
mPlanController = new RoutingPlanController(res, activity, planListener, listener);
mPlanController = new RoutingPlanController(res, activity, activity.startDrivingOptionsForResult, activity, activity);
return res;
}

View file

@ -2,8 +2,10 @@ package app.organicmaps.routing;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.content.Intent;
import android.os.Bundle;
import androidx.activity.result.ActivityResultLauncher;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@ -20,10 +22,11 @@ public class RoutingPlanInplaceController extends RoutingPlanController
private Animator mAnimator;
public RoutingPlanInplaceController(@NonNull MwmActivity activity,
ActivityResultLauncher<Intent> startDrivingOptionsForResult,
@NonNull RoutingPlanListener routingPlanListener,
@NonNull RoutingBottomMenuListener listener)
{
super(activity.findViewById(R.id.routing_plan_frame), activity, routingPlanListener, listener);
super(activity.findViewById(R.id.routing_plan_frame), activity, startDrivingOptionsForResult, routingPlanListener, listener);
mRoutingPlanListener = routingPlanListener;
}

View file

@ -1,4 +1,4 @@
package app.organicmaps.search;
package app.organicmaps.sdk.search;
import androidx.annotation.Keep;
import androidx.annotation.Nullable;
@ -6,11 +6,11 @@ import androidx.annotation.Nullable;
/**
* Native search will return results via this interface.
*/
public interface NativeBookmarkSearchListener
public interface BookmarkSearchListener
{
/**
* @param bookmarkIds Founded bookmark ids.
* @param timestamp Timestamp of search request.
* @param timestamp Timestamp of search request.
*/
// Used by JNI.
@Keep
@ -19,7 +19,7 @@ public interface NativeBookmarkSearchListener
/**
* @param bookmarkIds Founded bookmark ids.
* @param timestamp Timestamp of search request.
* @param timestamp Timestamp of search request.
*/
// Used by JNI.
@Keep

View file

@ -1,8 +1,8 @@
package app.organicmaps.search;
package app.organicmaps.sdk.search;
import androidx.annotation.NonNull;
class DisplayedCategories
public class DisplayedCategories
{
@NonNull
public static String[] getKeys()

View file

@ -0,0 +1,19 @@
package app.organicmaps.sdk.search;
import androidx.annotation.Keep;
import androidx.annotation.NonNull;
public interface MapSearchListener
{
// Called from JNI.
@Keep
@SuppressWarnings("unused")
record Result(String countryId, String matchedString)
{
}
// Called from JNI.
@Keep
@SuppressWarnings("unused")
void onMapSearchResults(@NonNull Result[] results, long timestamp, boolean isLast);
}

View file

@ -1,4 +1,4 @@
package app.organicmaps.search;
package app.organicmaps.sdk.search;
import android.os.Parcel;
import android.os.Parcelable;
@ -38,12 +38,12 @@ public class Popularity implements Parcelable
}
@Override
public void writeToParcel(Parcel dest, int flags)
public void writeToParcel(@NonNull Parcel dest, int flags)
{
dest.writeInt(this.mType.ordinal());
}
protected Popularity(Parcel in)
protected Popularity(@NonNull Parcel in)
{
int tmpMPopularity = in.readInt();
this.mType = Type.values()[tmpMPopularity];

View file

@ -1,4 +1,4 @@
package app.organicmaps.search;
package app.organicmaps.sdk.search;
import android.content.Context;
@ -14,9 +14,9 @@ import app.organicmaps.util.concurrency.UiThread;
import java.nio.charset.StandardCharsets;
public enum SearchEngine implements NativeSearchListener,
NativeMapSearchListener,
NativeBookmarkSearchListener
public enum SearchEngine implements SearchListener,
MapSearchListener,
BookmarkSearchListener
{
INSTANCE;
@ -30,7 +30,7 @@ public enum SearchEngine implements NativeSearchListener,
UiThread.run(
() ->
{
for (NativeSearchListener listener : mListeners)
for (SearchListener listener : mListeners)
listener.onResultsUpdate(results, timestamp);
});
}
@ -41,18 +41,18 @@ public enum SearchEngine implements NativeSearchListener,
UiThread.run(
() ->
{
for (NativeSearchListener listener : mListeners)
for (SearchListener listener : mListeners)
listener.onResultsEnd(timestamp);
});
}
@Override
public void onMapSearchResults(final NativeMapSearchListener.Result[] results, final long timestamp, final boolean isLast)
public void onMapSearchResults(@NonNull final MapSearchListener.Result[] results, final long timestamp, final boolean isLast)
{
UiThread.run(
() ->
{
for (NativeMapSearchListener listener : mMapListeners)
for (MapSearchListener listener : mMapListeners)
listener.onMapSearchResults(results, timestamp, isLast);
});
}
@ -60,55 +60,53 @@ public enum SearchEngine implements NativeSearchListener,
@Override
public void onBookmarkSearchResultsUpdate(@Nullable long[] bookmarkIds, long timestamp)
{
for (NativeBookmarkSearchListener listener : mBookmarkListeners)
for (BookmarkSearchListener listener : mBookmarkListeners)
listener.onBookmarkSearchResultsUpdate(bookmarkIds, timestamp);
}
@Override
public void onBookmarkSearchResultsEnd(@Nullable long[] bookmarkIds, long timestamp)
{
for (NativeBookmarkSearchListener listener : mBookmarkListeners)
for (BookmarkSearchListener listener : mBookmarkListeners)
listener.onBookmarkSearchResultsEnd(bookmarkIds, timestamp);
}
private final ObserverList<NativeSearchListener> mListeners = new ObserverList<>();
private final ObserverList<SearchListener> mListeners = new ObserverList<>();
private final ObserverList<NativeMapSearchListener> mMapListeners = new ObserverList<>();
private final ObserverList<MapSearchListener> mMapListeners = new ObserverList<>();
private final ObserverList<NativeBookmarkSearchListener> mBookmarkListeners = new ObserverList<>();
private final ObserverList<BookmarkSearchListener> mBookmarkListeners = new ObserverList<>();
public void addListener(NativeSearchListener listener)
public void addListener(SearchListener listener)
{
mListeners.addObserver(listener);
}
public void removeListener(NativeSearchListener listener)
public void removeListener(SearchListener listener)
{
mListeners.removeObserver(listener);
}
public void addMapListener(NativeMapSearchListener listener)
public void addMapListener(MapSearchListener listener)
{
mMapListeners.addObserver(listener);
}
public void removeMapListener(NativeMapSearchListener listener)
public void removeMapListener(MapSearchListener listener)
{
mMapListeners.removeObserver(listener);
}
public void addBookmarkListener(NativeBookmarkSearchListener listener)
public void addBookmarkListener(BookmarkSearchListener listener)
{
mBookmarkListeners.addObserver(listener);
}
public void removeBookmarkListener(NativeBookmarkSearchListener listener)
public void removeBookmarkListener(BookmarkSearchListener listener)
{
mBookmarkListeners.removeObserver(listener);
}
private native void nativeInit();
/**
*
* @param context
@ -116,7 +114,7 @@ public enum SearchEngine implements NativeSearchListener,
* @return whether search was actually started.
*/
@MainThread
public boolean search(@NonNull Context context, String query, boolean isCategory,
public boolean search(@NonNull Context context, @NonNull String query, boolean isCategory,
long timestamp, boolean hasLocation, double lat, double lon)
{
return nativeRunSearch(query.getBytes(StandardCharsets.UTF_8), isCategory,
@ -146,7 +144,7 @@ public enum SearchEngine implements NativeSearchListener,
}
@MainThread
public static void searchMaps(@NonNull Context context, String query, long timestamp)
public static void searchMaps(@NonNull Context context, @NonNull String query, long timestamp)
{
nativeRunSearchMaps(query.getBytes(StandardCharsets.UTF_8), Language.getKeyboardLocale(context),
timestamp);
@ -208,6 +206,8 @@ public enum SearchEngine implements NativeSearchListener,
nativeInit();
}
private native void nativeInit();
/**
* @param bytes utf-8 formatted bytes of query.
*/

View file

@ -1,4 +1,4 @@
package app.organicmaps.search;
package app.organicmaps.sdk.search;
import androidx.annotation.Keep;
import androidx.annotation.NonNull;
@ -6,10 +6,10 @@ import androidx.annotation.NonNull;
/**
* Native search will return results via this interface.
*/
public interface NativeSearchListener
public interface SearchListener
{
/**
* @param results Search results.
* @param results Search results.
* @param timestamp Timestamp of search request.
*/
// Called by JNI.

View file

@ -1,8 +1,7 @@
package app.organicmaps.search;
package app.organicmaps.sdk.search;
import android.content.Context;
import android.text.TextUtils;
import android.util.Pair;
import androidx.annotation.NonNull;
@ -50,6 +49,8 @@ public final class SearchRecents
}
private static native void nativeGetList(List<String> result);
private static native void nativeAdd(String locale, String query);
private static native void nativeClear();
}

View file

@ -1,4 +1,4 @@
package app.organicmaps.search;
package app.organicmaps.sdk.search;
import android.content.Context;
import android.graphics.Typeface;
@ -32,7 +32,7 @@ public class SearchResult
public static final int OPEN_NOW_NO = 2;
public static final SearchResult EMPTY = new SearchResult("", "", 0, 0,
new int[] {}, new int[] {});
new int[]{}, new int[]{});
// Used by JNI.
@Keep

View file

@ -18,6 +18,7 @@ import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.RecyclerView;
import app.organicmaps.R;
import app.organicmaps.sdk.search.DisplayedCategories;
import app.organicmaps.util.ThemeUtils;
import java.lang.annotation.Retention;
@ -26,9 +27,10 @@ import java.lang.annotation.RetentionPolicy;
class CategoriesAdapter extends RecyclerView.Adapter<CategoriesAdapter.ViewHolder>
{
@Retention(RetentionPolicy.SOURCE)
@IntDef({ TYPE_CATEGORY })
@interface ViewType {}
private static final int TYPE_CATEGORY = 0;
@IntDef({ ViewType.CATEGORY })
@interface ViewType {
int CATEGORY = 0;
}
@StringRes
private int[] mCategoryResIds;
@ -121,15 +123,16 @@ class CategoriesAdapter extends RecyclerView.Adapter<CategoriesAdapter.ViewHolde
@ViewType
public int getItemViewType(int position)
{
return TYPE_CATEGORY;
return ViewType.CATEGORY;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, @ViewType int viewType)
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, @ViewType int viewType)
{
View view;
ViewHolder viewHolder;
if (viewType == TYPE_CATEGORY)
if (viewType == ViewType.CATEGORY)
{
view = mInflater.inflate(R.layout.item_search_category, parent, false);
viewHolder = new ViewHolder(view, (TextView) view);

View file

@ -6,7 +6,8 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.view.ViewCompat;
import app.organicmaps.util.WindowInsetUtils;
import app.organicmaps.sdk.search.SearchEngine;
import app.organicmaps.util.WindowInsetUtils.PaddingInsetsListener;
import app.organicmaps.widget.SearchToolbarController;
import app.organicmaps.util.UiUtils;

View file

@ -1,26 +0,0 @@
package app.organicmaps.search;
import androidx.annotation.Keep;
public interface NativeMapSearchListener
{
// Called from JNI.
@Keep
@SuppressWarnings("unused")
class Result
{
public final String countryId;
public final String matchedString;
public Result(String countryId, String matchedString)
{
this.countryId = countryId;
this.matchedString = matchedString;
}
}
// Called from JNI.
@Keep
@SuppressWarnings("unused")
void onMapSearchResults(Result[] results, long timestamp, boolean isLast);
}

View file

@ -2,10 +2,6 @@ package app.organicmaps.search;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Typeface;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.style.StyleSpan;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@ -17,6 +13,7 @@ import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.RecyclerView;
import app.organicmaps.R;
import app.organicmaps.sdk.search.SearchResult;
import app.organicmaps.util.Graphics;
import app.organicmaps.util.ThemeUtils;
import app.organicmaps.util.UiUtils;

View file

@ -3,6 +3,7 @@ package app.organicmaps.search;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.location.Location;
import android.os.Bundle;
import android.text.TextUtils;
@ -10,6 +11,8 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.CallSuper;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@ -22,6 +25,7 @@ import androidx.recyclerview.widget.RecyclerView;
import androidx.viewpager.widget.ViewPager;
import app.organicmaps.Framework;
import app.organicmaps.MwmActivity;
import app.organicmaps.MwmApplication;
import app.organicmaps.R;
import app.organicmaps.base.BaseMwmFragment;
import app.organicmaps.bookmarks.data.FeatureId;
@ -31,6 +35,10 @@ import app.organicmaps.downloader.MapManager;
import app.organicmaps.location.LocationHelper;
import app.organicmaps.location.LocationListener;
import app.organicmaps.routing.RoutingController;
import app.organicmaps.sdk.search.SearchEngine;
import app.organicmaps.sdk.search.SearchListener;
import app.organicmaps.sdk.search.SearchRecents;
import app.organicmaps.sdk.search.SearchResult;
import app.organicmaps.util.Config;
import app.organicmaps.util.SharedPropertiesUtils;
import app.organicmaps.util.UiUtils;
@ -47,7 +55,7 @@ import java.util.Arrays;
import java.util.List;
public class SearchFragment extends BaseMwmFragment
implements NativeSearchListener,
implements SearchListener,
CategoriesAdapter.CategoriesUiListener
{
private long mLastQueryTimestamp;
@ -113,10 +121,9 @@ public class SearchFragment extends BaseMwmFragment
}
@Override
@SuppressWarnings("deprecation") // https://github.com/organicmaps/organicmaps/issues/3630
protected void startVoiceRecognition(Intent intent, int code)
protected void startVoiceRecognition(Intent intent)
{
startActivityForResult(intent, code);
startVoiceRecognitionForResult.launch(intent);
}
@Override
@ -168,6 +175,11 @@ public class SearchFragment extends BaseMwmFragment
private String mInitialLocale;
private boolean mInitialSearchOnMap = false;
private final ActivityResultLauncher<Intent> startVoiceRecognitionForResult = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), activityResult ->
{
mToolbarController.onVoiceRecognitionResult(activityResult);
});
private final LocationListener mLocationListener = new LocationListener()
{
@Override
@ -299,8 +311,10 @@ public class SearchFragment extends BaseMwmFragment
SearchEngine.INSTANCE.addListener(this);
SharedPreferences preferences = MwmApplication.prefs(requireContext());
int lastSelectedTabPosition = preferences.getInt(Config.KEY_PREF_LAST_SEARCHED_TAB, 0);
if (SearchRecents.getSize() == 0 && Config.isSearchHistoryEnabled())
pager.setCurrentItem(TabAdapter.Tab.CATEGORIES.ordinal());
pager.setCurrentItem(lastSelectedTabPosition);
tabAdapter.setTabSelectedListener(tab -> mToolbarController.deactivate());
@ -525,14 +539,6 @@ public class SearchFragment extends BaseMwmFragment
mToolbarController.showProgress(true);
}
@Override
@SuppressWarnings("deprecation") // https://github.com/organicmaps/organicmaps/issues/3630
public void onActivityResult(int requestCode, int resultCode, Intent data)
{
super.onActivityResult(requestCode, resultCode, data);
mToolbarController.onActivityResult(requestCode, resultCode, data);
}
@Override
public boolean onBackPressed()
{

View file

@ -11,6 +11,7 @@ import androidx.recyclerview.widget.RecyclerView;
import app.organicmaps.R;
import app.organicmaps.location.LocationHelper;
import app.organicmaps.routing.RoutingController;
import app.organicmaps.sdk.search.SearchRecents;
import app.organicmaps.widget.SearchToolbarController;
import app.organicmaps.util.Graphics;

View file

@ -1,6 +1,7 @@
package app.organicmaps.search;
import android.content.Context;
import android.content.SharedPreferences;
import android.util.SparseArray;
import android.view.View;
@ -11,6 +12,8 @@ import androidx.fragment.app.FragmentPagerAdapter;
import androidx.viewpager.widget.ViewPager;
import com.google.android.material.tabs.TabLayout;
import app.organicmaps.MwmApplication;
import app.organicmaps.R;
import app.organicmaps.util.Graphics;
import app.organicmaps.util.Config;
@ -91,6 +94,9 @@ class TabAdapter extends FragmentPagerAdapter
@Override
public void onTabSelected(TabLayout.Tab tab)
{
SharedPreferences.Editor editor = MwmApplication.prefs(mContext).edit();
editor.putInt(Config.KEY_PREF_LAST_SEARCHED_TAB, tab.getPosition());
editor.apply();
super.onTabSelected(tab);
Graphics.tint(mContext, tab.getIcon(), androidx.appcompat.R.attr.colorAccent);
}
@ -151,7 +157,9 @@ class TabAdapter extends FragmentPagerAdapter
ViewPager.OnPageChangeListener listener = new PageChangedListener(tabs);
mPager.addOnPageChangeListener(listener);
tabs.setOnTabSelectedListener(new OnTabSelectedListenerForViewPager(mPager));
listener.onPageSelected(0);
SharedPreferences preferences = MwmApplication.prefs(mPager.getContext());
int lastSelectedTabPosition = preferences.getInt(Config.KEY_PREF_LAST_SEARCHED_TAB, 0);
listener.onPageSelected(lastSelectedTabPosition);
}
void setTabSelectedListener(OnTabSelectedListener listener)

View file

@ -3,10 +3,9 @@ package app.organicmaps.settings;
import android.app.Activity;
import android.content.Intent;
import androidx.activity.result.ActivityResultLauncher;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import app.organicmaps.MwmActivity;
import app.organicmaps.base.BaseMwmFragmentActivity;
public class DrivingOptionsActivity extends BaseMwmFragmentActivity
@ -17,9 +16,9 @@ public class DrivingOptionsActivity extends BaseMwmFragmentActivity
return DrivingOptionsFragment.class;
}
public static void start(@NonNull Activity activity)
public static void start(@NonNull Activity activity, ActivityResultLauncher<Intent> startDrivingOptionsForResult)
{
Intent intent = new Intent(activity, DrivingOptionsActivity.class);
activity.startActivityForResult(intent, MwmActivity.REQ_CODE_DRIVING_OPTIONS);
startDrivingOptionsForResult.launch(intent);
}
}

View file

@ -32,7 +32,7 @@ import app.organicmaps.util.SharedPropertiesUtils;
import app.organicmaps.util.ThemeSwitcher;
import app.organicmaps.util.Utils;
import app.organicmaps.util.log.LogsManager;
import app.organicmaps.search.SearchRecents;
import app.organicmaps.sdk.search.SearchRecents;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import java.util.Locale;
@ -231,6 +231,8 @@ public class SettingsPrefsFragment extends BaseXmlSettingsFragment implements La
if (!SharedPropertiesUtils.shouldShowEmulateBadStorageSetting(requireContext()))
removePreference(getString(R.string.pref_settings_general), pref);
else
pref.setVisible(true);
}
private void initAutoZoomPrefsCallbacks()

View file

@ -1,5 +1,6 @@
package app.organicmaps.settings;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.content.res.Configuration;
@ -11,6 +12,8 @@ import android.text.style.ForegroundColorSpan;
import android.view.View;
import android.widget.Toast;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
@ -35,7 +38,6 @@ import java.util.Map;
public class VoiceInstructionsSettingsFragment extends BaseXmlSettingsFragment
{
private static final int REQUEST_INSTALL_DATA = 1;
@NonNull
@SuppressWarnings("NotNullFieldNotInitialized")
@ -67,6 +69,14 @@ public class VoiceInstructionsSettingsFragment extends BaseXmlSettingsFragment
private LanguageData mCurrentLanguage;
private String mSelectedLanguage;
private final ActivityResultLauncher<Intent> startInstallDataIntentForResult = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), activityResult -> {
if(activityResult.getResultCode() == Activity.RESULT_OK)
{
onInstallDataResult();
}
});
private final Preference.OnPreferenceChangeListener mEnabledListener = (preference, newValue) -> {
final boolean set = (Boolean) newValue;
if (!set)
@ -107,8 +117,7 @@ public class VoiceInstructionsSettingsFragment extends BaseXmlSettingsFragment
if (lang.downloaded)
setLanguage(lang);
else
UiUtils.startActivityForResult(VoiceInstructionsSettingsFragment.this,
new Intent(TextToSpeech.Engine.ACTION_INSTALL_TTS_DATA), REQUEST_INSTALL_DATA);
UiUtils.startActivityForResult(startInstallDataIntentForResult, new Intent(TextToSpeech.Engine.ACTION_INSTALL_TTS_DATA));
return false;
};
@ -181,21 +190,13 @@ public class VoiceInstructionsSettingsFragment extends BaseXmlSettingsFragment
updateTts();
}
@Override
@SuppressWarnings("deprecation") // https://github.com/organicmaps/organicmaps/issues/3630
public void onActivityResult(int requestCode, int resultCode, Intent data)
private void onInstallDataResult()
{
// Do not check resultCode here as it is always RESULT_CANCELED
super.onActivityResult(requestCode, resultCode, data);
updateTts();
if (requestCode == REQUEST_INSTALL_DATA)
{
updateTts();
LanguageData lang = mLanguages.get(mSelectedLanguage);
if (lang != null && lang.downloaded)
setLanguage(lang);
}
LanguageData lang = mLanguages.get(mSelectedLanguage);
if (lang != null && lang.downloaded)
setLanguage(lang);
}
private void enableListeners(boolean enable)

View file

@ -7,7 +7,6 @@ import android.os.Build;
import androidx.annotation.NonNull;
import androidx.preference.PreferenceManager;
import app.organicmaps.BuildConfig;
import app.organicmaps.MwmActivity;
import app.organicmaps.MwmApplication;
import app.organicmaps.R;
@ -36,6 +35,8 @@ public final class Config
private static final String KEY_DONATE_URL = "DonateUrl";
private static final String KEY_PREF_SEARCH_HISTORY = "SearchHistoryEnabled";
public static final String KEY_PREF_LAST_SEARCHED_TAB = "LastSearchTab";
/**
* The total number of app launches.
*/

View file

@ -118,13 +118,14 @@ public class LocationUtils
return ContextCompat.checkSelfPermission(context, ACCESS_FINE_LOCATION) == PERMISSION_GRANTED;
}
public static boolean checkCoarseLocationPermission(@NonNull Context context)
/**
* Checks if the app has location permissions granted.
* Returns true if either:
* Only ACCESS_COARSE_LOCATION is granted
* Both ACCESS_COARSE_LOCATION and ACCESS_FINE_LOCATION are granted
*/
public static boolean checkLocationPermission(@NonNull Context context)
{
return ContextCompat.checkSelfPermission(context, ACCESS_COARSE_LOCATION) == PERMISSION_GRANTED;
}
public static boolean checkLocationPermission(@NonNull Context context)
{
return checkFineLocationPermission(context) || checkCoarseLocationPermission(context);
}
}

View file

@ -10,6 +10,7 @@ import androidx.annotation.NonNull;
import app.organicmaps.MwmApplication;
import app.organicmaps.R;
import java.text.NumberFormat;
import java.util.Locale;
public class StringUtils
@ -24,11 +25,31 @@ public class StringUtils
return String.format(Locale.getDefault(), pattern, args);
}
/**
* This method returns correct string representation of % (percent number) for different locales
* For example, that's how the result of would look like:
* formatPercent(0.2319) will return 23.19% (in US locale)
* formatPercent(0.2319) will return %23.19 (in Turkish locale)
* formatPercent(0.2319) will return 23,19% (in Latvian locale)
* formatPercent(1.23) will return 123%
* formatPercent(0.000145) will return 0.01%
* formatPercent(0.37) will return 37%
*
* @param fraction a double value, that represents a fraction of a whole
* @return correct string representation of percent for different locales
*/
public static String formatPercent(double fraction) {
NumberFormat percentFormat = NumberFormat.getPercentInstance();
percentFormat.setMaximumFractionDigits(2);
return percentFormat.format(fraction);
}
public static native boolean nativeIsHtml(String text);
public static native boolean nativeContainsNormalized(String str, String substr);
public static native String[] nativeFilterContainsNormalized(String[] strings, String substr);
public static native int nativeFormatSpeed(double metersPerSecond);
public static native Pair<String, String> nativeFormatSpeedAndUnits(double metersPerSecond);
public static native Distance nativeFormatDistance(double meters);
@NonNull

View file

@ -9,6 +9,7 @@ import android.graphics.Color;
import android.graphics.Rect;
import android.os.Build;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.view.TouchDelegate;
import android.view.View;
import android.view.ViewGroup;
@ -18,6 +19,8 @@ import android.view.WindowManager;
import android.widget.Button;
import android.widget.TextView;
import androidx.activity.result.ActivityResult;
import androidx.activity.result.ActivityResultLauncher;
import androidx.annotation.AnyRes;
import androidx.annotation.AttrRes;
import androidx.annotation.ColorInt;
@ -32,7 +35,6 @@ import androidx.core.graphics.Insets;
import androidx.core.view.WindowCompat;
import androidx.core.view.WindowInsetsCompat;
import androidx.core.view.WindowInsetsControllerCompat;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.RecyclerView;
import app.organicmaps.MwmApplication;
import app.organicmaps.R;
@ -207,6 +209,15 @@ public final class UiUtils
return context.getResources().getDimensionPixelSize(id);
}
// this method returns the total height of the display (in pixels) including notch and other touchable areas
public static int getDisplayTotalHeight(Context context)
{
DisplayMetrics metrics = new DisplayMetrics();
WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
windowManager.getDefaultDisplay().getRealMetrics(metrics);
return metrics.heightPixels;
}
public static void updateRedButton(Button button)
{
button.setTextColor(ThemeUtils.getColor(button.getContext(), button.isEnabled() ? R.attr.redButtonTextColor
@ -362,10 +373,9 @@ public final class UiUtils
}
}
@SuppressWarnings("deprecation") // https://github.com/organicmaps/organicmaps/issues/3630
public static void startActivityForResult(@NonNull Fragment fragment, @NonNull Intent intent, int requestCode)
public static void startActivityForResult(ActivityResultLauncher<Intent> startForResult, @NonNull Intent intent)
{
fragment.startActivityForResult(intent, requestCode);
startForResult.launch(intent);
}
// utility class

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