Compare commits

..

288 Commits

Author SHA1 Message Date
x7z4w
b8f532b1d8 [traffxml] Use m2::PointF for junctions
Signed-off-by: x7z4w <x7z4w@noreply.codeberg.org>
2025-11-08 18:16:59 +00:00
mvglasow
a9700156db [traffic] Keep polling and processing messages while routing
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-11-08 17:49:46 +02:00
mvglasow
1d87e0e987 [traffic] Cache routing session state, introduce IsObserverInhibited()
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-11-08 16:32:28 +02:00
mvglasow
f7882636cd [traffic] Documentation
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-11-08 16:28:34 +02:00
mvglasow
40fbea11e7 [traffic][android] Keep Proguard from deleting TraFF classes
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-11-07 00:41:30 +02:00
mvglasow
e87727c168 [traffic] Add missing override, silence warning
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-11-06 22:29:06 +02:00
mvglasow
d82b545e30 [traffic][android] Move Java source files to android/sdk
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-11-06 21:09:26 +02:00
mvglasow
781d973faa Merge commit '2601ec854a' into traffic
# Conflicts:
#	iphone/Maps/Model/Settings.swift
#	iphone/Maps/UI/Settings/SettingsNavigationView.swift
2025-11-05 23:06:07 +02:00
mvglasow
d6818786f7 [traffic] Vary junction search radius depending on road class
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-11-04 20:33:08 +02:00
x7z4w
615f57c604 [traffxml] Faster MessageFromXml
Signed-off-by: x7z4w <x7z4w@noreply.codeberg.org>
2025-11-03 20:21:50 +01:00
mvglasow
af20e2b42b [traffic] Truncate decoded location to nearby junctions
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-11-03 20:26:04 +02:00
mvglasow
05f6dfad7b [traffic] Dyamically determine radius in which to search for junctions
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-11-03 20:26:04 +02:00
mvglasow
2b867a64a0 [traffic] Reduce weight for fake segments involving junctions
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-11-03 20:26:04 +02:00
mvglasow
c0fd405798 [traffic] Refactor methods for penalty calculation
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-11-03 20:26:04 +02:00
mvglasow
207d6c833d [traffic] Pass toJunctions parameter to TruncateRoute()
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-11-03 20:26:04 +02:00
mvglasow
d47aa09053 [traffxml] Process fuzziness attribute in location
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-10-27 22:50:05 +02:00
mvglasow
fbc150fae2 [routing] Documentation
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-10-26 18:23:10 +02:00
mvglasow
d88ed01bc1 [traffic] Update comment on offroad penalty
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-10-26 14:40:47 +02:00
mvglasow
3ec32e4415 [traffic] Use references for loop, not copies
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-10-25 15:12:05 +03:00
mvglasow
9065f45b21 [traffic] Exclude roundabouts from decoded locations (with exceptions)
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-10-25 15:09:28 +03:00
mvglasow
46d363ae24 [routing] Documentation
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-10-25 14:49:49 +03:00
mvglasow
0dc62c47dd [traff_assessment_tool] Show decoding time
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-10-24 21:44:38 +03:00
mvglasow
3c9eeb9a75 [traff_assessment_tool] Introduce status line for traffic panel
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-10-21 21:13:26 +03:00
mvglasow
ae23afd72e [traff_assessment_tool] Hide progress bar only on final update
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-10-21 00:07:57 +03:00
mvglasow
800cc0641b [traff_assessment_tool] Refactor TrafficModel constructor
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-10-20 23:55:29 +03:00
mvglasow
1b74062447 [traff_assessment_tool] Show animation while feed is loading
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-10-20 23:44:48 +03:00
mvglasow
6a694c5d3e [traffic] Documentation and comment cleanup
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-10-20 22:47:26 +03:00
mvglasow
dda13b8d3d [traffic] Penalize turns near endpoints
This improves decoding quality on urban multi-carriageway roads.

Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-10-20 22:38:29 +03:00
mvglasow
c1340a9941 [traffic] Truncate locations which overshoot their endpoints
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-10-19 22:39:06 +03:00
mvglasow
a313526aed [routing] Documentation
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-10-19 18:02:04 +03:00
mvglasow
32d1a3a36e [traffic] Eliminate any ETA calculations, always return weight
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-10-19 17:59:48 +03:00
mvglasow
3a45740884 [routing] Documentation
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-10-19 17:45:35 +03:00
mvglasow
4e624bd04b [traffic] Comment out unused argument
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-10-19 17:45:25 +03:00
mvglasow
a3d1ed83c3 [routing][traffic] Different routing options in navigation and decoder mode
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-10-18 16:34:32 +03:00
mvglasow
38dbad0f7e [traffic] Add test cases
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-10-18 15:24:00 +03:00
mvglasow
7fe5823140 [traffic] Optimize offroad penalty in decoder
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-10-17 21:09:54 +03:00
Jean-Baptiste
2601ec854a Release notes for 1st October release
Signed-off-by: Jean-Baptiste <jeanbaptiste.charron@outlook.fr>
2025-10-16 12:56:33 +02:00
Jean-Baptiste
3fbecd00d5 [android] Fix aligment of turns lanes view in landscape
Signed-off-by: Jean-Baptiste <jeanbaptiste.charron@outlook.fr>
2025-10-14 20:54:35 +02:00
mvglasow
091d510ba1 [traffic] Simplify ferry landing penalty in TraFF decoder
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-10-14 21:52:12 +03:00
mvglasow
f07c8d66d8 [traffic] Calculate segment weight based on road ref
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-10-14 21:41:22 +03:00
Yannik Bloscheck
c9f50cdc72 [ios] Enabled traffic button
Signed-off-by: Yannik Bloscheck <git@yannikbloscheck.com>
2025-10-14 10:54:37 +02:00
Codeberg Translate
26cdde144a [strings] Update from Codeberg Translate
Co-authored-by: B o d o <timtrek@noreply.codeberg.org>
Co-authored-by: Codeberg Translate <translate@codeberg.org>
Co-authored-by: Edgarsons <edgarsons@noreply.codeberg.org>
Co-authored-by: Juno Takano <jutty@noreply.codeberg.org>
Co-authored-by: Mickael81 <mickael81@noreply.codeberg.org>
Co-authored-by: Outbreak2096 <outbreak2096@noreply.codeberg.org>
Co-authored-by: Prefill add-on <noreply-addon-prefill@weblate.org>
Co-authored-by: Priit Jõerüüt <jrtcdbrg@noreply.codeberg.org>
Co-authored-by: Stephan-P <stephan-p@noreply.codeberg.org>
Co-authored-by: Weblate <noreply-mt-weblate@weblate.org>
Co-authored-by: Weblate Translation Memory <noreply-mt-weblate-translation-memory@weblate.org>
Co-authored-by: codebergian42 <codebergian42@noreply.codeberg.org>
Co-authored-by: dobridabar <dobridabar@noreply.codeberg.org>
Co-authored-by: ghose <ghose@noreply.codeberg.org>
Co-authored-by: ikanakova <ikanakova@noreply.codeberg.org>
Co-authored-by: lazlo <lazlo@noreply.codeberg.org>
Co-authored-by: ldmpub <ldmpub@noreply.codeberg.org>
Co-authored-by: map-per <map-per@noreply.codeberg.org>
Co-authored-by: omarhassan <omarhassan@noreply.codeberg.org>
Co-authored-by: pL1 <pl1@noreply.codeberg.org>
Co-authored-by: summoner <summoner@noreply.codeberg.org>
Co-authored-by: summoner001 <summoner@disroot.org>
Co-authored-by: x7z4w <x7z4w@noreply.codeberg.org>
Translate-URL: https://translate.codeberg.org/projects/comaps/android/
Translation: CoMaps/Android - Map Feature Types
Translation: CoMaps/Android UI Strings
Translation: CoMaps/Countries and regions names
Translation: CoMaps/Search synonyms / aliases
Translation: CoMaps/iOS - Map Feature Types
Translation: CoMaps/iOS UI Strings
2025-10-14 06:09:25 +00:00
Jean-Baptiste
f9a0dd1cb2 [android] Add offline explanation on download dialog
Signed-off-by: Jean-Baptiste <jeanbaptiste.charron@outlook.fr>
2025-10-14 06:34:38 +02:00
Jean-Baptiste
bdde42ce4e [android] Hide socket cardview in editor
Signed-off-by: Jean-Baptiste <jeanbaptiste.charron@outlook.fr>
2025-10-13 21:47:59 +02:00
map-per
633896854d Improve place does not exist dialog
Signed-off-by: map-per <map-per@gmx.de>
2025-10-13 21:45:15 +02:00
Yannik Bloscheck
37a518cb9b [ios] Add UI for avoiding steps
Signed-off-by: Yannik Bloscheck <git@yannikbloscheck.com>
2025-10-13 20:07:31 +02:00
x7z4w
dd3b6faa42 [android] Add UI for avoiding steps
Signed-off-by: x7z4w <x7z4w@noreply.codeberg.org>
2025-10-13 20:07:31 +02:00
x7z4w
fceb6e0b8a [routing] Avoid steps
Signed-off-by: x7z4w <x7z4w@noreply.codeberg.org>
2025-10-13 20:07:31 +02:00
Jean-Baptiste
ad07875b88 [android] Fix crash in BookmarkCategoriesSettings
Signed-off-by: Jean-Baptiste <jeanbaptiste.charron@outlook.fr>
2025-10-13 19:50:27 +02:00
x7z4w
db1e5131b7 [core] Drop products
Signed-off-by: x7z4w <x7z4w@noreply.codeberg.org>
2025-10-13 12:09:17 +00:00
Yannik Bloscheck
ba7addc504 [ios] Adding offline explanation
Signed-off-by: Yannik Bloscheck <git@yannikbloscheck.com>
2025-10-13 10:56:01 +02:00
Séverin Lemaignan
d168855ef5 [android] add support for editing charging stations details
While here, uncluttered a little the charging station editor,
removing fields like the POI address which is basically irrelevant for
charging stations.

Signed-off-by: Séverin Lemaignan <severin@guakamole.org>
2025-10-12 20:17:06 +02:00
Séverin Lemaignan
509ff4de72 ChargeSocketsHelper: implement a Diff method between old and new OSM keys
Signed-off-by: Séverin Lemaignan <severin@guakamole.org>
2025-10-12 20:17:06 +02:00
mvglasow
2ed9bc1880 [traffic] Properly process message replacement
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-10-12 16:39:44 +03:00
x7z4w
340b5ae3f9 [routing] Update mwm_hierarchy_handler
Signed-off-by: x7z4w <x7z4w@noreply.codeberg.org>
2025-10-12 10:59:49 +02:00
mvglasow
d098ecae15 [traff_assessment_tool] Ensure traffic layer is enabled
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-10-11 19:02:43 +03:00
Jean-Baptiste
f5afe7f363 [android] Fix color of opening hours icon
Signed-off-by: Jean-Baptiste <jeanbaptiste.charron@outlook.fr>
2025-10-11 15:55:52 +02:00
mvglasow
9e06ec815e [traff_assessment_tool] Handle invalid selections gracefully, clear marks
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-10-11 15:08:17 +03:00
x7z4w
51bfa3d372 [android] Fixup
Signed-off-by: x7z4w <x7z4w@noreply.codeberg.org>
2025-10-11 14:07:21 +02:00
Andrei Shkrob
91d523a6fa [android-auto] Fix InvalidDisplayException
Signed-off-by: Andrei Shkrob <github@shkrob.dev>
2025-10-11 14:07:21 +02:00
Andrei Shkrob
1c8eeeeee2 [android-auto] Fix display switching in navigation mode
Signed-off-by: Andrei Shkrob <github@shkrob.dev>
2025-10-11 14:07:21 +02:00
Andrei Shkrob
034098f5ef [android-auto] Fix routing arrow offset
Signed-off-by: Andrei Shkrob <github@shkrob.dev>
2025-10-11 14:07:21 +02:00
Andrei Shkrob
3fcf653deb [android-auto] Log screens' lifecycle
Signed-off-by: Andrei Shkrob <github@shkrob.dev>
2025-10-11 14:07:21 +02:00
Andrei Shkrob
5035aa3f79 [android] Fix theme change
Signed-off-by: Andrei Shkrob <github@shkrob.dev>
2025-10-11 14:07:21 +02:00
Andrei Shkrob
c887abfd80 [android-auto] Optimize imports
Signed-off-by: Andrei Shkrob <github@shkrob.dev>
2025-10-11 14:07:21 +02:00
Andrei Shkrob
707415a788 [android-auto] Fix activity recreation when [dis]connecting to car
Signed-off-by: Andrei Shkrob <github@shkrob.dev>
2025-10-11 14:07:21 +02:00
Andrei Shkrob
832273f928 [android-auto] Refactor SurfaceRendered
Created base Renderer class
Rename SurfaceRenderer -> SurfaceRendererLegacy

Signed-off-by: Andrei Shkrob <github@shkrob.dev>
2025-10-11 14:07:21 +02:00
Andrei Shkrob
1947a32007 [android] Allow call to getHolder() in MapView
Signed-off-by: Andrei Shkrob <github@shkrob.dev>
2025-10-11 14:07:21 +02:00
Viktor Govako
e73c37a5dd [android] Improve AA route simulator.
Signed-off-by: Viktor Govako <viktor.govako@gmail.com>
2025-10-11 14:07:21 +02:00
Andrei Shkrob
9912d19302 [android] Get rid of MapFragment
Signed-off-by: Andrei Shkrob <github@shkrob.dev>
2025-10-11 14:07:21 +02:00
Viktor Govako
30f624a070 [android] Follow up after trackVisibleViewport=true.
Signed-off-by: Viktor Govako <viktor.govako@gmail.com>
2025-10-11 14:07:21 +02:00
Andrei Shkrob
428ff30af7 [android] Proper usage of dark theme
Signed-off-by: Andrei Shkrob <github@shkrob.dev>
2025-10-11 14:07:21 +02:00
Andrei Shkrob
f514606ebe [android] New layers icons
Signed-off-by: Andrei Shkrob <github@shkrob.dev>
2025-10-11 14:07:21 +02:00
mvglasow
bf6cf27f8c [traff_assessment_tool] Clear markers when updating TraFF data
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-10-11 14:47:41 +03:00
Jean-Baptiste
e0351b26ba [android] Fix color of edit phone button in the editor
Signed-off-by: Jean-Baptiste <jeanbaptiste.charron@outlook.fr>
2025-10-11 12:41:52 +02:00
Jean-Baptiste
64f8d751cf [android] Improve top UI navigation
Signed-off-by: Jean-Baptiste <jeanbaptiste.charron@outlook.fr>
2025-10-10 20:01:25 +02:00
mvglasow
bf36c875d5 Merge commit 'eb264889e3' into traffic 2025-10-10 18:40:35 +03:00
ikanakova
d145f3b51b Aktualizovat libs/search/query_params.cpp
Signed-off-by: ikanakova <ikanakova@noreply.codeberg.org>
2025-10-09 22:29:55 +02:00
ikanakova
f42014cf37 Removing duplicates in query_params.cpp
Signed-off-by: ikanakova <ikanakova@noreply.codeberg.org>
2025-10-09 22:29:55 +02:00
Codeberg Translate
66fae205c2 [strings] Update from Codeberg Translate
Co-authored-by: Codeberg Translate <translate@codeberg.org>
Co-authored-by: Weblate Translation Memory <noreply-mt-weblate-translation-memory@weblate.org>
Co-authored-by: codebergian42 <codebergian42@noreply.codeberg.org>
Co-authored-by: ikanakova <ikanakova@noreply.codeberg.org>
Co-authored-by: omarhassan <omarhassan@noreply.codeberg.org>
Co-authored-by: ovl-005 <ovl-005@noreply.codeberg.org>
Co-authored-by: strebski <strebski@noreply.codeberg.org>
Co-authored-by: summoner <summoner@noreply.codeberg.org>
Co-authored-by: summoner001 <summoner@disroot.org>
Co-authored-by: x7z4w <x7z4w@noreply.codeberg.org>
Translation: CoMaps/Android - Map Feature Types
Translation: CoMaps/Android UI Strings
Translation: CoMaps/Android UI Strings (SDK)
Translation: CoMaps/Countries and regions names
Translation: CoMaps/F-Droid app description
Translation: CoMaps/Google Play and Huawei AppGallery descriptions
Translation: CoMaps/Search synonyms / aliases
Translation: CoMaps/iOS - Map Feature Types
Translation: CoMaps/iOS UI Strings
2025-10-09 22:01:57 +02:00
Yannik Bloscheck
54ba143bbe [ios] Fix main search bar background
Signed-off-by: Yannik Bloscheck <git@yannikbloscheck.com>
2025-10-09 20:09:03 +02:00
Yannik Bloscheck
c685b8bed3 [ios] Fix search bar background for cuisines
Signed-off-by: Yannik Bloscheck <git@yannikbloscheck.com>
2025-10-09 20:08:40 +02:00
mvglasow
30b2df89cd [routing] Documentation
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-10-09 20:44:33 +03:00
Jean-Baptiste
bb94621cdc [strings] Fix first letter in lowercase in countries translations
Signed-off-by: Jean-Baptiste <jeanbaptiste.charron@outlook.fr>
2025-10-09 18:58:31 +02:00
x7z4w
17f34d155e [docs] Update
Signed-off-by: x7z4w <x7z4w@noreply.codeberg.org>
2025-10-09 16:53:12 +02:00
x7z4w
175e971064 [search] Add Lithuanian synonyms
Signed-off-by: x7z4w <x7z4w@noreply.codeberg.org>
2025-10-09 11:42:56 +02:00
Codeberg Translate
533e34f390 [strings] Update from Codeberg Translate
Co-authored-by: Codeberg Translate <translate@codeberg.org>
Co-authored-by: Juno Takano <jutty@noreply.codeberg.org>
Co-authored-by: Weblate <noreply-mt-weblate@weblate.org>
Co-authored-by: cirilla <cirilla@noreply.codeberg.org>
Co-authored-by: codebergian42 <codebergian42@noreply.codeberg.org>
Co-authored-by: dmlls <dmlls@noreply.codeberg.org>
Co-authored-by: ghose <ghose@noreply.codeberg.org>
Co-authored-by: x7z4w <x7z4w@noreply.codeberg.org>
Translate-URL: https://translate.codeberg.org/projects/comaps/android/
Translate-URL: https://translate.codeberg.org/projects/comaps/search-synonyms-aliases/
Translation: CoMaps/Android UI Strings
Translation: CoMaps/Search synonyms / aliases
Translation: CoMaps/iOS Plurals
2025-10-09 07:44:25 +00:00
Jean-Baptiste
24ec765a00 [android] Remove confirmation dialog before to logout
Signed-off-by: Jean-Baptiste <jeanbaptiste.charron@outlook.fr>
2025-10-08 20:36:23 +02:00
eisa01
e5b6d882bf [android] Fix Metadata IntRange
Discovered while debugging issue #2078

Signed-off-by: eisa01 <eisa01@gmail.com>
2025-10-08 19:59:10 +02:00
Séverin Lemaignan
a74ae0fa3a [android] display info about available sockets on charging stations
This commit includes SVG icons of the sockets that are currently
supported. This icons have been created for this specific occasion.

Signed-off-by: Séverin Lemaignan <severin@guakamole.org>
2025-10-08 19:35:32 +02:00
Jean-Baptiste
431852f8cc [android] Fix crash on the place page
Signed-off-by: Jean-Baptiste <jeanbaptiste.charron@outlook.fr>
2025-10-08 19:14:53 +02:00
ikanakova
a21a717bec [search] Add Czech synonyms 2025-10-08 19:07:28 +02:00
x7z4w
d7221bdfc9 [ios] Remove "Add Business" dead code
Signed-off-by: x7z4w <x7z4w@noreply.codeberg.org>
2025-10-08 17:54:25 +02:00
map-per
efc8e44350 merge add place/business options
Signed-off-by: map-per <map-per@gmx.de>
2025-10-08 17:54:25 +02:00
omarhassan
e3c287db53 [search] Add more Arabic synonyms
Signed-off-by: omarhassan <omarhassan@noreply.codeberg.org>
Co-authored-by: omarhassan <omarhassan@noreply.codeberg.org>
Co-committed-by: omarhassan <omarhassan@noreply.codeberg.org>
2025-10-08 17:41:27 +02:00
Diego Miguel
0748e4c9a4 Review all Spanish categories strings
Signed-off-by: Diego Miguel <dmlls@diegomiguel.me>
2025-10-08 10:55:37 +02:00
Codeberg Translate
7739329cb4 [strings] Update from Codeberg Translate
Co-authored-by: Alexey Ladygin <nitrodox@noreply.codeberg.org>
Co-authored-by: B o d o <timtrek@noreply.codeberg.org>
Co-authored-by: Codeberg Translate <translate@codeberg.org>
Co-authored-by: Grimpy101 <grimpy101@noreply.codeberg.org>
Co-authored-by: Lenny Angst <lenny@familie-angst.ch>
Co-authored-by: Prefill add-on <noreply-addon-prefill@weblate.org>
Co-authored-by: Ricky-Tigg <ricky-tigg@noreply.codeberg.org>
Co-authored-by: Weblate Translation Memory <noreply-mt-weblate-translation-memory@weblate.org>
Co-authored-by: Wizard-pope <wizard-pope@noreply.codeberg.org>
Co-authored-by: brtc <brtc@noreply.codeberg.org>
Co-authored-by: codebergian42 <codebergian42@noreply.codeberg.org>
Co-authored-by: fbausch <fbausch@noreply.codeberg.org>
Co-authored-by: map-per <map-per@noreply.codeberg.org>
Co-authored-by: om_Yanto <om_yanto@noreply.codeberg.org>
Co-authored-by: omarhassan <omarhassan@noreply.codeberg.org>
Co-authored-by: ovl-005 <ovl-005@noreply.codeberg.org>
Co-authored-by: phpcoder_istanbul <phpcoder_istanbul@noreply.codeberg.org>
Co-authored-by: rimas <rimas@noreply.codeberg.org>
Co-authored-by: sas33 <sas33@noreply.codeberg.org>
Co-authored-by: summoner <summoner@noreply.codeberg.org>
Co-authored-by: x7z4w <x7z4w@noreply.codeberg.org>
Translation: CoMaps/Android - Map Feature Types
Translation: CoMaps/Android UI Strings
Translation: CoMaps/Android UI Strings (SDK)
Translation: CoMaps/Apple AppStore description
Translation: CoMaps/Countries and regions names
Translation: CoMaps/F-Droid app description
Translation: CoMaps/Google Play and Huawei AppGallery descriptions
Translation: CoMaps/Search synonyms / aliases
Translation: CoMaps/Voice announcements for navigation (TTS)
Translation: CoMaps/iOS - Map Feature Types
Translation: CoMaps/iOS Plist
Translation: CoMaps/iOS Plurals
Translation: CoMaps/iOS UI Strings
2025-10-07 13:09:56 +00:00
Yannik Bloscheck
2619738882 [ios] Add charger files to Xcode
Signed-off-by: Yannik Bloscheck <git@yannikbloscheck.com>
2025-10-06 14:07:46 +02:00
Séverin Lemaignan
2217db002a refactor: libs/feature_helpers merged in libs/indexer
Signed-off-by: Séverin Lemaignan <severin@guakamole.org>
2025-10-06 14:07:46 +02:00
Jean-Baptiste
00be9a536d [android] Use M3 style on stop button
Signed-off-by: Jean-Baptiste <jeanbaptiste.charron@outlook.fr>
2025-10-05 22:07:14 +02:00
Henry Sternberg
ed37b6595e Amend route snapping distance for car
Signed-off-by: Henry Sternberg <henry@bluelightmaps.com>
2025-10-05 12:07:09 +01:00
Jean-Baptiste
e670bef0a2 [android] Migrate action button to Material 3
Signed-off-by: Jean-Baptiste <jeanbaptiste.charron@outlook.fr>
2025-10-05 12:41:46 +02:00
Yannik Bloscheck
ccd3f9813a [ios] Use same icon for track recording in menu and custom main button
Signed-off-by: Yannik Bloscheck <git@yannikbloscheck.com>
2025-10-03 20:57:01 +02:00
Yannik Bloscheck
db6ad05406 [ios] Fix hang on launch in certain conditions related to the main buttons
Signed-off-by: Yannik Bloscheck <git@yannikbloscheck.com>
2025-10-03 20:54:40 +02:00
Kiryl Kaveryn
8520f32f2d [ios] The viewport is calculated using the safe area insets
Instead of manual origin and frame calculation. It fixes the issue when the location arrow and road are not centered while navigation.

Signed-off-by: Kiryl Kaveryn <kirylkaveryn@gmail.com>
2025-10-03 20:09:06 +02:00
Kiryl Kaveryn
47f16c5b89 [ios] Skip CarPlay VC from updating the viewport when CP was disabed
The updateVisibleViewPortToDefaultState was called from the `viewDidLayoutSubviews` when the VC is removed from the stack, updates the viewport unexpectedly and overwrite the correct viewport with CP screen size. It may break the viewport when switching to the phone.

Signed-off-by: Kiryl Kaveryn <kirylkaveryn@gmail.com>
2025-10-03 20:09:06 +02:00
Kiryl Kaveryn
056711f4bd [ios] Update the visual scale with the current screen's value on switch to phone
When the app was not running and launched from the Carplay, the main_visualScale was initialized with CP scale (usually it is 2). Then, when the user switches to the phone this incorrect values can be used in `UpdateVisualScale`. The current screen's scale should be used instead in the `updateVisualScaleToMain` without intermediated values.

Signed-off-by: Kiryl Kaveryn <kirylkaveryn@gmail.com>
2025-10-03 20:09:06 +02:00
x7z4w
2eccbba26e [search] Add Spanish synonyms
Signed-off-by: x7z4w <x7z4w@noreply.codeberg.org>
2025-10-03 15:13:31 +02:00
Yannik Bloscheck
8101d50f50 [ios] Change button from "Done" to "Continue" on the first step of adding a location
Signed-off-by: Yannik Bloscheck <git@yannikbloscheck.com>
2025-10-03 15:10:59 +02:00
Yannik Bloscheck
8a0a069195 [search] Add English (US) search terms for cinemas
Signed-off-by: Yannik Bloscheck <git@yannikbloscheck.com>
2025-10-03 15:10:27 +02:00
map-per
a26f4933dc Adjust editor details fields and add level to more categories
Signed-off-by: map-per <map-per@gmx.de>
2025-10-03 12:29:35 +02:00
Yannik Bloscheck
aa2d35759f [styles] Add specific icon for bus stations
Signed-off-by: Yannik Bloscheck <git@yannikbloscheck.com>
2025-10-03 12:26:38 +02:00
Séverin Lemaignan
233f5b4c33 add tests for the charge socket processing code
Signed-off-by: Séverin Lemaignan <severin@guakamole.org>
2025-10-03 12:24:43 +02:00
Séverin Lemaignan
284972ad60 add support for multiple powers per socket type
Both when reading from OSM and when generating OSM keys.

Signed-off-by: Séverin Lemaignan <severin@guakamole.org>
2025-10-03 12:24:43 +02:00
Séverin Lemaignan
0c0e9ab5f6 refactor charge sockets data processing into dedicated class
While here, add method to generate the OSM keys corresponding
to a socket list.

Signed-off-by: Séverin Lemaignan <severin@guakamole.org>
2025-10-03 12:24:43 +02:00
Séverin Lemaignan
e7f766084f sort the charging socket information from most to least powerful
Signed-off-by: Séverin Lemaignan <severin@guakamole.org>
2025-10-03 12:24:43 +02:00
mvglasow
ba2d653a30 [3party/protobuf] Fix faulty merge
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-09-28 13:55:34 +03:00
mvglasow
d6eacd7364 [drape] Correctly render traffic at zoom 20
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-09-26 22:53:08 +03:00
mvglasow
07c75e627e [traff_assessment_tool] Move to tools
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-09-12 21:40:35 +03:00
mvglasow
e7bde9aa05 [openlr_decoder] Fix faulty merge
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-09-12 21:27:21 +03:00
mvglasow
5ba708caff [map] Documentation
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-09-12 19:06:21 +03:00
mvglasow
48e8f32e01 [traff_assessment_tool] Use different marker colors for reference points
Also fixes bug with DebugMarkPoint no longer rendering after the last merge

Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-09-12 18:46:50 +03:00
mvglasow
38e98df6cc Merge commit '05cc660641' into traffic
# Conflicts:
#	CMakeLists.txt
#	android/app/src/main/java/app/organicmaps/settings/SettingsPrefsFragment.java
#	android/sdk/src/main/cpp/app/organicmaps/sdk/Framework.hpp
#	android/sdk/src/main/cpp/app/organicmaps/sdk/OrganicMaps.cpp
#	android/sdk/src/main/cpp/app/organicmaps/sdk/util/Config.cpp
#	libs/indexer/data_source.hpp
#	libs/indexer/feature.hpp
#	libs/indexer/ftypes_matcher.hpp
#	libs/map/framework.cpp
#	libs/map/traffic_manager.cpp
#	libs/routing/absent_regions_finder.cpp
#	libs/routing/edge_estimator.hpp
#	libs/routing/index_router.cpp
#	libs/routing/index_router.hpp
#	libs/routing/routing_session.hpp
#	libs/routing_common/num_mwm_id.hpp
#	libs/traffic/traffic_info.cpp
#	qt/mainwindow.hpp
#	qt/preferences_dialog.cpp
#	tools/openlr/helpers.hpp
#	tools/openlr/openlr_decoder.cpp
#	tools/openlr/openlr_decoder.hpp
#	tools/openlr/openlr_stat/openlr_stat.cpp
#	tools/openlr/router.hpp
#	tools/openlr/score_candidate_paths_getter.cpp
#	tools/openlr/score_candidate_paths_getter.hpp
#	xcode/CoMaps.xcworkspace/contents.xcworkspacedata
2025-09-10 21:22:40 +03:00
mvglasow
0713e22328 [traffic] Set default traffic URL to dev server
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-09-01 22:02:45 +03:00
mvglasow
3ed31a575f [traffic] Inhibit perodic traffic updates during route calculation
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-08-29 22:12:33 +03:00
mvglasow
ac87e3c585 [traffic] Handle MWM removal during traffic update
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-08-29 17:49:15 +03:00
mvglasow
ef806cf18a Synchronize map updates with traffic manager
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-08-28 21:10:42 +03:00
mvglasow
d46c0fec76 [traffic] Fix endless loop in TrafficManager::Invalidate()
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-08-24 22:15:30 +03:00
mvglasow
1d42d3b431 [traff_assessment_tool] On click, zoom to message and show reference points
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-08-19 20:03:04 +03:00
mvglasow
2663eda820 [traff_assessment_tool] Basic traffic message panel
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-08-19 00:16:17 +03:00
mvglasow
16708aae7f [traff_assessment_tool] Rename TrafficMode to TrafficModel
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-08-17 18:08:05 +03:00
mvglasow
6963637b1b [traffxml] Fix compiler warnings
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-08-16 19:59:28 +03:00
mvglasow
57f55f1022 [traffxml] Fix attribute names in output
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-08-16 19:52:03 +03:00
Eivind Samseth
01476d3dc7 Standard format changes 2025-08-16 17:59:47 +02:00
mvglasow
90d7cadc3f Merge commit '211e3fb4' into traffic
# Conflicts:
#	android/app/src/main/cpp/CMakeLists.txt
#	android/app/src/main/java/app/organicmaps/sdk/util/Config.java
#	android/app/src/main/java/app/organicmaps/settings/SettingsPrefsFragment.java
#	android/app/src/main/java/app/organicmaps/widget/placepage/sections/PlacePageLinksFragment.java
#	android/sdk/src/main/cpp/app/organicmaps/sdk/OrganicMaps.cpp
#	indexer/ftypes_matcher.hpp
#	iphone/Maps/Maps.xcodeproj/project.pbxproj
2025-08-16 18:04:14 +03:00
mvglasow
3a6f21dbd1 [traffic][android] Implement Android TraFF source
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-08-16 14:59:22 +03:00
mvglasow
221fe69840 Merge remote-tracking branch 'upstream/traffic' into traffic 2025-08-12 19:04:30 +03:00
mvglasow
167f0b8377 Add traffic.xml to .gitignore
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-08-12 19:02:46 +03:00
mvglasow
df7d507e1b WIP: [traffic][android] Implement Android TraFF source
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-08-12 00:29:04 +03:00
mvglasow
fe737602d8 [traffxml] Documentation
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-08-10 18:58:34 +03:00
mvglasow
daa147a721 [traffxml] Provide virtual destructor for TraffSource
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-08-10 18:58:20 +03:00
mvglasow
bd555afe61 [traffxml] Remove unused HttpTraffSource::ThreadRoutine()
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-08-10 18:57:52 +03:00
Yannik Bloscheck
73a70d943e [traffic][ios] Make HttpTraffSource configurable in Preferences
Signed-off-by: Yannik Bloscheck <git@yannikbloscheck.com>
2025-08-06 17:21:12 +02:00
mvglasow
dde50bd0a1 [traffic] Default URL
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-29 20:39:57 +03:00
mvglasow
0106dc3fe5 [traffic] Remove unused constants
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-29 20:39:05 +03:00
mvglasow
034856f033 Merge commit '20c9fc5f' into traffic-2025072304
# Conflicts:
#	android/app/src/main/res/xml/prefs_main.xml
#	qt/CMakeLists.txt
2025-07-28 18:27:49 +03:00
mvglasow
f53c794fdd [traffic] Add missing qualifier
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 01:00:43 +03:00
mvglasow
dcab6ee5a0 [qt] Keep traffic URL editable even when source is disabled
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:26 +03:00
mvglasow
edc15ac982 [traffic][android] Make HttpTraffSource configurable in Preferences
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:26 +03:00
mvglasow
871cd73592 [traffic] Make traffic initialization work with LoadMapsAsync()
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:25 +03:00
mvglasow
958be3dee6 [traffxml] Fix segfault when deleting the last vector element
v.erase(v.end()) is incorrect and will crash on clang but works on gcc

Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:25 +03:00
mvglasow
06f63dcb9a [traffxml] Fix compiler warning
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:25 +03:00
mvglasow
bebac8d8a7 [traffxml] Remove openlr dependency, disable OpenLrV3TraffDecoder
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:25 +03:00
mvglasow
a25602dbe0 [traffxml] Return to features supported by Clang 20.1 for IsoTime
This reverts commit 776444edc7c4730f67e8aa2fa30b983c73e01054.

# Conflicts:
#	traffxml/traff_model.cpp
2025-07-28 00:33:25 +03:00
mvglasow
98796cd6f8 [android] Link against traffxml
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:25 +03:00
mvglasow
2729d07732 [traffic] Fix assignment
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:25 +03:00
mvglasow
61b15d623d [traffxml] Fix compiler warnings
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:25 +03:00
mvglasow
03d6847be3 [traffic][qt] Make HttpTraffSource configurable from Qt GUI
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:25 +03:00
mvglasow
3b1fca01e3 [traffic] API to reconfigure a running HttpTrafficSource
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:25 +03:00
mvglasow
be3792b93a [traffic] Remove obsolete code
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:25 +03:00
mvglasow
7283e4ecb4 [traffic] Read HttpTrafficSource parameters from config
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:25 +03:00
mvglasow
75c7d146af [traffic] Unsubscribe when traffic manager is disabled
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:25 +03:00
mvglasow
d988ab3326 [traffic] Restore decoded segments from cache on startup
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:25 +03:00
mvglasow
798affe0ef [traffxml] Documentation
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:25 +03:00
mvglasow
93a1f9d1a6 [traffxml] Fix bug when storing point distance attribute
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:25 +03:00
mvglasow
3f58c6ee20 [traffic] Implement HttpTraffSource
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:25 +03:00
mvglasow
a20d1453e0 [traffic] Documentation
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:25 +03:00
mvglasow
e825753487 [traffic] Remove obsolete code
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:25 +03:00
mvglasow
cc58eaa50a [traffic] Restore and document enable/disable/pause/resume logic
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:25 +03:00
mvglasow
75197a11a8 [traffic] Consider routing MWMs when updating subscriptions on resume
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:25 +03:00
mvglasow
d03b47bee0 [traffic] Refactoring
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:25 +03:00
mvglasow
daf344b27f [traffic] Remove m_mwmCache and related logic
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:24 +03:00
mvglasow
121bdc4af8 [traffic] Do not announce traffic updates if nothing has changed
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:24 +03:00
mvglasow
6656c7e441 [traffxml] Make sources pluggable
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:24 +03:00
mvglasow
f32493faaa [traffxml] Comment and documentation cleanup
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:24 +03:00
mvglasow
4f4d376a4a [traffic] Comment out unused code
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:24 +03:00
mvglasow
4324e329e5 [routing] Documentation
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:24 +03:00
mvglasow
9eeac05fdf [traffic] Update routing MWMs as route changes
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:24 +03:00
mvglasow
b418cf659c Documentation
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:24 +03:00
mvglasow
964368f5d4 [traffic] Replace full invalidation with subscription recalculation
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:24 +03:00
mvglasow
23922f1c2b [traffic] Invalidate per MWM on download (untested)
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:24 +03:00
mvglasow
f02b1538e7 Documentation
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:24 +03:00
mvglasow
dd65e89f8f [traffic] Feature-complete cache persistence, including decoded coloring
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:24 +03:00
mvglasow
f132022e60 [traffxml] Documentation
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:24 +03:00
mvglasow
247f88254e [traffxml] Fix erroneous parsing of event length
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:24 +03:00
mvglasow
81a31d6b42 [traffxml] Documentation and comments
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:24 +03:00
mvglasow
89d1365fee [traffxml] Make some arguments const &
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:23 +03:00
mvglasow
9fb08bdc56 [traffxml] Store message cache in file
So far only API and tests, and without decoded segments

Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:23 +03:00
mvglasow
371a58f6f9 [traffic] Use traff_storage to read hardcoded poll feeds
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:23 +03:00
mvglasow
af8b748c59 [traffxml] Add traff_storage
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:23 +03:00
mvglasow
a43e83d280 [traff_assessment_tool] Use path of last file (if any) for file dialog
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:23 +03:00
mvglasow
04b2059ca0 [traffic] workaround for drape bug when updating segments
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:23 +03:00
mvglasow
ed15925251 [traffxml] Remove some log output
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:23 +03:00
mvglasow
173b5e1718 [traffic] Update clear/purge logic to use update mechanism
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:23 +03:00
mvglasow
26aa5e5f54 [traffic] Handle removed segments or eased traffic impact
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:23 +03:00
mvglasow
f31541efb2 [traffxml] Purge expired messages
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:23 +03:00
mvglasow
db3ed87b92 [traff_assessment_tool] Update window title
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:23 +03:00
mvglasow
fbaa5470fd [traff_assessment_tool] Shift timestamps read from TraFF files
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:23 +03:00
mvglasow
0681171d69 [traffxml] Introduce timestamp shift operation
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:23 +03:00
mvglasow
e3d86be324 [traffxml] Use std::chrono:utc_clock for IsoTime, improve parsing
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:23 +03:00
mvglasow
ef3de2c781 [traffxml] Use std::chrono::system_clock for IsoTime
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:23 +03:00
mvglasow
d574b536ba [traffxml] Fix ISO8601 parser regex
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:22 +03:00
mvglasow
2d3ca8014b Documentation
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:22 +03:00
mvglasow
df13e279b6 [traffic] Override EdgeEstimator::CalcOffroad()
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:22 +03:00
mvglasow
b48310e6a5 [traffxml] Parse duration quantifier and use it for delays
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:22 +03:00
mvglasow
b98fe1999c Documentation
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:22 +03:00
mvglasow
d47713516d [traffxml] Ensure decoder uses newly-added maps
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:22 +03:00
mvglasow
d72bd9e00e [traffic] Update traffic for all MWMs, active or not
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:22 +03:00
mvglasow
8cffe8fa64 [traffic] Documentation and comments
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:22 +03:00
mvglasow
ae5dea4a53 [traffic] Comment out more obsolete code
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:22 +03:00
mvglasow
588332a23b [traffic] Remove dead code
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:22 +03:00
mvglasow
3eb99e952c [map] Documentation and comments
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:22 +03:00
mvglasow
9f4b6d73ce [traff_assessment_tool] Add UI for Clear()
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:22 +03:00
mvglasow
488159e2f9 [traffic] Implement Clear()
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:22 +03:00
mvglasow
f30316d868 [traff_assessment_tool] Open TraFF feed
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:22 +03:00
mvglasow
daaf52d27d [traffic] Fix Push() and make it public for testing
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:22 +03:00
mvglasow
ba9980ba36 [traffic] Introduce test mode for traffic manager
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:22 +03:00
mvglasow
5531b1129b [traffxml] Code comment
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:22 +03:00
mvglasow
394a6673e5 [traffxml] Silence compiler warning
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:21 +03:00
mvglasow
62ee9d5b46 [traffic] Abort event loop run immediately if TrafficManager is disabled
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:21 +03:00
mvglasow
2592bcf042 WIP: [traffxml] traff_assessment_tool, based on openlr_assessment_tool
Skeleton without any traff functionality so far
Contains some obsolete code, commented out with #ifdef openlr_obsolete

Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:21 +03:00
mvglasow
dd7ed98c1a [traffic] Use enabled state instead of Start()
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:21 +03:00
mvglasow
76fce016bb [traffxml] Process delay in traffic impact
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:21 +03:00
mvglasow
7db32a9922 [traffxml] Do not request additional maps during TraFF decoding
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:21 +03:00
mvglasow
fa5608d874 [traffxml] Decode point locations (at)
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:21 +03:00
mvglasow
185febd8d8 [traffic] Documentation and comments
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:21 +03:00
mvglasow
7a5ea64ea0 [traffxml] Score candidates based on road attributes
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:21 +03:00
mvglasow
a4106505af [traffxml] Code cleanup
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:21 +03:00
mvglasow
083845a502 [traffxml] Fix location matching on dual carriageway roads
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:21 +03:00
mvglasow
c6de2a25aa [traffic] Documentation
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:21 +03:00
mvglasow
4c5fb21c33 [traffic] Use distances, not travel time, for weight in TraffEstimator
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:21 +03:00
mvglasow
f566f6f0ef [traffxml] Use custom EdgeEstimator for decoding
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:21 +03:00
mvglasow
9afb28aaa1 [traffxml] Add router-based decoder, still crude, ugly and buggy
To use it, redefine DefaultTraffDecoder in traffxml/traff_decoder.hpp

Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:21 +03:00
mvglasow
bd178932c1 [traffic] Refactor TraFF decoder into separate class
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:21 +03:00
mvglasow
2894218573 [traffic] Use LFRCNP, derived from FRC
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:21 +03:00
mvglasow
63f0799161 [traffic] Calculate DNP from nominal distance, if available and plausible
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:21 +03:00
mvglasow
5b67d668bd [traffic] Refactor message decoding
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:21 +03:00
mvglasow
f7adea08a2 [traffic] Documentation
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:21 +03:00
mvglasow
c0c8d5da58 [traffxml] Parse and store distance for location points
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:20 +03:00
mvglasow
2ed300ca08 [traffic] Comment cleanup
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:20 +03:00
mvglasow
52a915211e [traffic] Remove mwms from ThreadRoutine()
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:20 +03:00
mvglasow
18f1dfac45 [traffxml] Documentation
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:20 +03:00
mvglasow
a7897e2347 [traffic] Calculate filter list for active MWMs
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:20 +03:00
mvglasow
e3f5dd3ca8 [traffic] Throttle UI/router refresh while messages are being processed
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:20 +03:00
mvglasow
de03995e77 [openlr] Modify openlr_stat to work with single data source
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:20 +03:00
mvglasow
74d79e5c8e [traffic] Reuse previously decoded segments and coloring, where possible
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:20 +03:00
mvglasow
e2aff53291 [traffic] Comparison operators for TrafficImpact, TraffLocation and Point
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:20 +03:00
mvglasow
a39bdee0d1 [traffic] Refactor IsoTime comparison operators
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:20 +03:00
mvglasow
136293c308 [traffic] Add IsoTime::IsPast()
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:20 +03:00
mvglasow
356b051036 [traffic] Re-enable message deduplication between feed queue and cache
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:20 +03:00
mvglasow
c8d5a07262 [traffic] Defer TrafficManager startup until MWMs are first updated
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:20 +03:00
mvglasow
e94c23d538 [traffic] Insert mew messages into cache but skip deduplication for now
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:20 +03:00
mvglasow
2ba3030366 [traffic] Remove forgotten InitializeDataSources() method
Obsolete since we started using a single data source in 5a031c55

Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:20 +03:00
mvglasow
3455050876 [traffic] Forgotten hunk of 9f39d3bc (store coloring with message)
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:20 +03:00
mvglasow
cf57942a0b [traffic] Allow decoding to be interrupted after each message
Message deduplication currently disabled

Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:20 +03:00
mvglasow
3a713c477a [traffic] Refactor IsoTime into a class
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:20 +03:00
mvglasow
edb1b7e784 [traffic] Consolidate feed queue before decoding messages
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:20 +03:00
mvglasow
53e80b9283 [traffic] Refactor m_feeds to m_feedQueue
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:20 +03:00
mvglasow
7107314e2f [traffic] Store colorings with message and build global coloring from that
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:20 +03:00
mvglasow
fafec070c9 [traffic] Use MWM ID for Coloring map, now that we have a single DataSource
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:20 +03:00
mvglasow
d7facd5732 [openlr] Initialize OpenLR decoder with a single DataSource
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:20 +03:00
mvglasow
382e46af63 [routing] Documentation
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:20 +03:00
mvglasow
d0a9c564e4 [traffic] Process TrafficImpact::m_maxspeed
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:19 +03:00
mvglasow
a9ceec3995 [traffic] Initialize TrafficManager with a DataSource
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:19 +03:00
mvglasow
73d61ff655 [traffic] Store TraFF message ID with decoded path
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:19 +03:00
mvglasow
5cdf14386d [traffxml] Set OpenLR FOW for ramps
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:19 +03:00
mvglasow
2f6a8564cb [openlr] Evaluate FOW for Sliproad/*_link
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:19 +03:00
mvglasow
80a7ed503e [traffxml] Tweak GuessDnp
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:19 +03:00
mvglasow
6e65e60c3d [traffxml] Set FRC on all OpenLR location reference points
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:19 +03:00
mvglasow
f041f910e7 [openlr] Documentation
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:19 +03:00
mvglasow
dbf253c9d1 [openlr] Always evaluate FRC and FOW, regardless of LinearSegment source
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:19 +03:00
mvglasow
24d65bd37f WIP: [traffic] Implement basic TraFF parsing, currently from hardcoded path
Not feature complete, produces incorrect results for some test cases
Some parts of the implementation are not very elegant yet
Inefficient as the whole set of messages is parsed on update
Lots of verbose debug logging
Lots of dead code from old traffic module (#ifdef traffic_dead_code)

Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:19 +03:00
mvglasow
16cb70a952 [traffic] Add TrafficInfo constructor with Coloring
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:19 +03:00
mvglasow
8827ec3c09 [indexer] Documentation
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:19 +03:00
mvglasow
7be0b8a256 [traffxml] Adhere to naming convention for member names
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:19 +03:00
mvglasow
f0f847b214 [openlr] Debug output for FunctionalRoadClass
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:19 +03:00
mvglasow
2017907b1f [traffic] Documentation
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:19 +03:00
mvglasow
bb410fc3bc [traffxml] Add module
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:19 +03:00
mvglasow
6e8d400611 [traffic] Include OpenLR headers in traffic_manager.cpp
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:19 +03:00
mvglasow
7b420def17 [qt] Link against openlr library
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:19 +03:00
mvglasow
737d7b5643 [traffic] Initialize TrafficManager with CountryParentNameGetterFn
Signed-off-by: mvglasow <michael -at- vonglasow.com>

# Conflicts:
#	map/framework.cpp
#	map/traffic_manager.cpp
#	map/traffic_manager.hpp
2025-07-28 00:33:19 +03:00
mvglasow
9c93f421ac [traffic] Add documentation
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-07-28 00:33:19 +03:00
mvglasow
1574b5b7cb Revert "[routing] Do not create TrafficStash instance."
This reverts commit 3c12ba5134f2aa9d19c0c9a54af89d368f389eb4.
2025-07-28 00:33:18 +03:00
mvglasow
932dda6552 Revert "[desktop] Disable traffic switch and TrafficManager initialization."
This reverts commit 16ad61f4c8ebd22bdc282496122db49a5243f02f.
2025-07-28 00:33:18 +03:00
mvglasow
6f2f61b30a Revert "[qt] Remove defunct Traffic layer button"
This reverts commit df2541e1bf12abca329becdac8de7c92f0893b03.
2025-07-28 00:33:08 +03:00
Konstantin Pastbin
20c9fc5f45 [fdroid] Release version 2025.07.23-4
Signed-off-by: Konstantin Pastbin <konstantin.pastbin@gmail.com>
2025-07-23 21:00:52 +07:00
Konstantin Pastbin
be3e3d773b [android] Fix Panoramax links not working
Signed-off-by: Konstantin Pastbin <konstantin.pastbin@gmail.com>
2025-07-23 20:59:37 +07:00
Konstantin Pastbin
0fd7f8d573 [fdroid] Release version 2025.07.23-2
Signed-off-by: Konstantin Pastbin <konstantin.pastbin@gmail.com>
2025-07-23 10:50:00 +07:00
679 changed files with 20651 additions and 5140 deletions

1
.gitignore vendored
View File

@@ -20,6 +20,7 @@ data/symbols/**/symbols.sdf
data/bookmarks
data/edits.xml
data/traffic.xml
data/World.mwm
data/WorldCoasts.mwm
data/world_mwm/*

View File

@@ -28,3 +28,8 @@
# R8 crypts the source line numbers in all log messages.
# https://github.com/organicmaps/organicmaps/issues/6559#issuecomment-1812039926
-dontoptimize
# Keep classes for Android TraFF support
-keep class app.organicmaps.sdk.traffxml.SourceImplV0_7 { *; }
-keep class app.organicmaps.sdk.traffxml.SourceImplV0_8 { *; }

View File

@@ -0,0 +1,9 @@
• Vylepšena viditelnost a uživatelské rozhraní pokynů v navigaci
• Přidána možnost vynechat kroky
• Vylepšeno vyhledávání ve více jazycích
• Přidána specifická ikona pro autobusové zastávky
• Opraveny problémy s Android Auto (prostřednictvím projektu OM)
• Vylepšen editor a opraveny drobné problémy
• Vylepšeny styly map (prostřednictvím projektu OM)
• Vylepšeny překlady aplikace
Další změny najdete v našich poznámkách k vydání Codeberg!

View File

@@ -1,10 +1,9 @@
Karten-Daten vom 6. September
Zeitstrafen für Radrouting
Farbige POI-Beschriftungen in Autonavigation
POI-Suche nach Filialnamen
Abspannportale, Sicherheitskabinen & Büros hinzugefügt
Aktualisierte Symbole für Türme, Nachtclubs, Apotheken und Fahnenmasten
Lesezeichen-Farbe in GPX-Exporten
Farbfüllung von Fußgängerbereiche auf Plätzen
• Android Auto: Anzeige Ausfahrtsnummern in Kreisverkehren
Weitere Änderungen in Codeberg-Versionshinweisen!
Verbesserte Sichtbarkeit & Benutzeroberfläche für Navigationsanweisungen
Option um Treppen zu vermeiden
Verbesserte Suche in mehreren Sprachen
Spezifisches Symbol für Busbahnöfe hinzugefügt
Probleme mit Android Auto behoben (via OM)
Verbesserter Editor mit kleinere Bugfixes
Kartenstile verbessert (via OM)
Verbesserte Übersetzungen
Für weitere Änderungen siehe Codeberg-Versionshinweise

View File

@@ -1,10 +1,9 @@
OpenStreetMap data as of September 6
Turn penalties for bicycle routing
Colored POI labels for car navigation map style
Search POIs by branch name
Added power portals, security booths and offices
Update icons for towers, nightclubs, chemists, flagpoles
Save bookmark color to GPX exports
Color fill pedestrian parts of squares
• Android Auto: display roundabout exit numbers
Improved visibility and UI of instructions in navigation
Added option to avoid steps
Improved search in multiple languages
Added specific icon for bus stations
Fixed Android Auto issues (via OM project)
Improved editor and fix minor issues
Improved map styles (via OM project)
Improved app translations
Check our Codeberg release notes for more changes!

View File

@@ -1,10 +1,8 @@
Datos a 6 de septiembre
Penalizaciones de giros en rutas de bici
Etiquetas de puntos de interés en color en navegación
Buscar por nombre de sucursal
Añadir portales de energía, cabinas de seguridad y oficinas
Nuevos iconos: torres, discotecas, parafarmacias y mástiles
Guardar color en exportaciones GPX
• Colorear áreas peatonales de plazas
• Android Auto: mostrar números de salida de rotondas
Mejora de la visibilidad y la interfaz de usuario de las instrucciones de navegación
Se ha añadido la opción de evitar escaleras
Mejora de la búsqueda en varios idiomas como ES
Se ha añadido un icono específico para las estaciones de autobús
Se han solucionado los problemas de Android Auto (a través del proyecto OM)
Se ha mejorado el editor y se han solucionado pequeños problemas
Se han mejorado los estilos de los mapas (a través del proyecto OM)
Más detalles en Codeberg

View File

@@ -1,9 +1,9 @@
Données OSM du 06 septembre
Pénalités dans les calculs d'itinéraires vélos
Ajout de labels colorés en mode navigation
Support du tag branch dans la recherche
Ajout des portiques électriques, postes de sécurité
Mise à jour des icônes de tours, boîtes de nuits, chimiste et mat
Enregistrement de la couleur des signets dans les exports GPX
Android Auto: Affichage du numéro de sortie des ronds-points
Interface utilisateur et visibilité des instructions en navigation améliorée
Option pour éviter les escaliers ajoutée
Recherche améliorée dans différents languages
Icône pour les gares routières ajoutée
Corrections de bugs liées à Android Auto (via OM)
Editeur amélioré et corrections de bugs
Style de la carte amélioré (via OM)
Traductions améliorées
Plus d'informations sur notre Codeberg

View File

@@ -0,0 +1,32 @@
Aplikasi peta gratis & sumber terbuka yang dipimpin komunitas, berbasis data OpenStreetMap dan diperkuat dengan komitmen terhadap transparansi, privasi, serta non-profit. CoMaps adalah turunan dari Organic Maps, yang merupakan turunan dari Maps.ME.
Baca lebih lanjut tentang alasan proyek ini dan arahnya di <b><i>codeberg.org/comaps</i></b>.
Bergabunglah dengan komunitas dan bantu menjadikan aplikasi peta terbaik
• Gunakan aplikasi ini dan sebarkan
• Beri masukan dan laporkan masalah
• Perbarui data peta di aplikasi atau di situs OpenStreetMap
‣ <b>Fokus Offline</b>: Rencanakan dan navigasikan perjalananmu di luar negeri tanpa perlu layanan seluler, cari titik saat hiking jauh, dll. Semua fungsi aplikasi dirancang untuk bekerja offline.
‣ <b>Menghormati Privasi</b>: Aplikasi ini dirancang dengan privasi sebagai prioritas tidak mengidentifikasi orang, tidak melacak, dan tidak mengumpulkan informasi pribadi. Bebas iklan.
‣ <b>Sederhana dan Rapi</b>: fitur penting yang mudah digunakan dan langsung berfungsi.
‣ <b>Hemat Baterai dan Ruang</b>: Tidak menguras baterai seperti aplikasi navigasi lain. Peta ringkas menghemat ruang berharga di ponselmu.
‣ <b>Gratis dan Dibangun oleh Komunitas</b>: Orang seperti kamu membantu membangun aplikasi ini dengan menambahkan tempat ke OpenStreetMap, menguji serta memberi masukan fitur, dan menyumbangkan keterampilan pengembangan maupun dana.
‣ <b>Terbuka dan Transparan</b>: Pengambilan keputusan dan keuangan transparan, non-profit, dan sepenuhnya sumber terbuka.
<b>Fitur Utama</b>:
• Peta detail yang bisa diunduh, dengan tempat yang tidak ada di Google Maps
• Mode outdoor dengan sorotan jalur hiking, area berkemah, sumber air, puncak, garis kontur, dll
• Jalur pejalan kaki dan jalur sepeda
• Titik menarik seperti restoran, SPBU, hotel, toko, tempat wisata, dan banyak lagi
• Pencarian berdasarkan nama, alamat, atau kategori titik menarik
• Navigasi dengan suara untuk berjalan, bersepeda, atau berkendara
• Tandai tempat favoritmu dengan sekali tap
• Artikel Wikipedia offline
• Layer dan rute transportasi subway
• Rekaman jejak
• Ekspor dan impor bookmark serta jejak dalam format KML, KMZ, GPX
• Mode gelap untuk digunakan saat malam
• Tingkatkan data peta untuk semua orang dengan editor bawaan sederhana
<b>Kebebasan Ada di Sini</b>
Temukan perjalananmu, jelajahi dunia dengan privasi dan komunitas di garis depan!

View File

@@ -0,0 +1 @@
Navigasi peta mudah Temukan lebih banyak Didukung oleh komunitas

View File

@@ -1,10 +0,0 @@
• OpenStreetMap aggiornato al 6 Settembre
• Bici: miglior stima del tempo di percorrenza
• Auto: punti d'interesse colorati
• Cerca i punti d'interesse tramite filiale
• Aggiunte strutture alta tensione, cabine di sicurezza e uffici
• Aggiornate le icone per torri, discoteche, farmacie e aste per bandiere
• Salva il colore dei Preferiti nel GPX
• Evidenziate le aree pedonali nelle piazze
• AndroidAuto: aggiunto il numero dell'uscita nelle rotonde
Visita Codeberg per ulteriori dettagli

View File

@@ -1 +1 @@
Lett kart navigasjon - Opplev mere på din reise - Drevet av felleskapet
Enkel kartnavigering - Opplev mere på din reise - Drevet av felleskapet

View File

@@ -1 +1 @@
CoMaps - Gå tur, sykkel, kjør - med personvern
CoMaps - Gå, sykle, kjøre offline med personvern

View File

@@ -1 +1 @@
Łatwa nawigacja Odkryj więcej ze swojej podróży Wspierane przez społeczność
Łatwa nawigacja po mapie - Odkryj więcej z podróży - Wspierane przez społeczność

View File

@@ -1,10 +1,9 @@
Dados OSM de 6/09
Penalidades de conversão para bicicleta
Etiquetas de POI coloridas para o estilo do mapa de navegação de carros
Busca de POIs por nome de filial
Adição de portais de energia, guaritas e escritórios
Atualização de ícones para torres, discotecas, farmácias e mastros
Salva a cor dos favoritos nas exportações GPX
Preenchimento de praças pavimentadas
• Android Auto: exibe números de saída de rotatória
Confira nossas notas de lançamento do Codeberg para mais mudanças
Visibilidade e interface do usuário aprimoradas para instruções na navegação
Opção adicionada para evitar degraus
Busca aprimorada em vários idiomas
Adição de ícone específico para rodoviárias
Problemas corrigidos no Android Auto (via projeto OM)
Editor aprimorado e correção de problemas menores
Estilos de mapa aprimorados (via projeto OM)
Traduções aprimoradas
Confira nossas notas de lançamento do Codeberg para mais mudanças!

View File

@@ -1,10 +1,9 @@
Данные на 6 сентября
Предупреждения на веломаршруте
Цветные метки POI при автонавигации
Поиск POI по названию ветки
Добавлены высоковольтные опоры, помещения охраны и офисы
Обновлены значки башен,вышек,мачт,ночных клубов,аптек
Сохранение цвета закладок при экспорте в GPX (через OM)
Отображение цветом пешеходных зон
• Android Auto: отображение номеров съездов на кольцевых развязках
Посмотрите примечания к выпуску Codeberg, чтобы узнать о других изменениях!
Лучшая видимость и интерфейс при навигации
Добавлена возможность пропускать шаги
Улучшен поиск на нескольких языках
Новый значок автостанций
Исправлены проблемы с Android Auto (через OM)
Улучшен редактор и исправлены мелкие недочёты
Улучшены стили карт (через OM)
Улучшены переводы приложения
Ознакомьтесь с примечаниями к выпуску Codeberg, чтобы узнать изменениях!

View File

@@ -1 +1 @@
version: 2025.10.08-3-FDroid+25100803
version: 2025.07.23-4-FDroid+25072304

View File

@@ -0,0 +1 @@
Navigasi peta mudah Temukan lebih banyak Didukung oleh komunitas

View File

@@ -0,0 +1 @@
CoMaps - Jelajah dengan Privat

View File

@@ -0,0 +1 @@
Enkel kartnavigering - Opplev mere på din reise - Drevet av felleskapet

View File

@@ -0,0 +1 @@
CoMaps -Naviger med personvern

View File

@@ -0,0 +1 @@
Łatwa nawigacja po mapie - Odkryj więcej z podróży - Wspierane przez społeczność

View File

@@ -62,6 +62,21 @@
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https" />
</intent>
<intent>
<action android:name="org.traffxml.traff.GET_CAPABILITIES"/>
</intent>
<intent>
<action android:name="org.traffxml.traff.POLL"/>
</intent>
<intent>
<action android:name="org.traffxml.traff.SUBSCRIBE"/>
</intent>
<intent>
<action android:name="org.traffxml.traff.SUBSCRIPTION_CHANGE"/>
</intent>
<intent>
<action android:name="org.traffxml.traff.UNSUBSCRIBE"/>
</intent>
</queries>
<supports-screens
@@ -89,6 +104,7 @@
<!-- Allows for config and orientation change without killing/restarting main activity -->
<activity
android:name="app.organicmaps.SplashActivity"
android:theme="@style/MwmTheme.Splash"
android:configChanges="orientation|screenSize|smallestScreenSize|density|screenLayout|uiMode|keyboard|keyboardHidden|navigation"
android:screenOrientation="fullUser"
android:exported="true">
@@ -349,6 +365,7 @@
<activity
android:name="app.organicmaps.DownloadResourcesLegacyActivity"
android:theme="@style/MwmTheme.DownloadResourcesLegacy"
android:configChanges="orientation|screenLayout|screenSize"
android:screenOrientation="fullUser"/>
@@ -365,6 +382,7 @@
<activity
android:name="app.organicmaps.MwmActivity"
android:theme="@style/MwmTheme.MainActivity"
android:launchMode="singleTask"
android:configChanges="uiMode"
android:screenOrientation="fullUser"
@@ -386,6 +404,7 @@
<activity
android:name="app.organicmaps.search.SearchActivity"
android:theme="@style/MwmTheme.CardBg"
android:configChanges="orientation|screenLayout|screenSize"
android:screenOrientation="fullUser"
android:label="@string/search_map"
@@ -412,6 +431,7 @@
<activity
android:name="app.organicmaps.bookmarks.BookmarkCategoriesActivity"
android:theme="@style/MwmTheme.WindowBg"
android:configChanges="orientation|screenLayout|screenSize"
android:screenOrientation="fullUser"
android:label="@string/bookmarks_and_tracks"
@@ -420,6 +440,7 @@
<activity
android:name="app.organicmaps.bookmarks.BookmarkListActivity"
android:theme="@style/MwmTheme.CardBg"
android:configChanges="orientation|screenLayout|screenSize"
android:screenOrientation="fullUser"
android:label="@string/bookmarks"

View File

@@ -24,7 +24,6 @@ import androidx.annotation.CallSuper;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.annotation.StyleRes;
import androidx.core.view.ViewCompat;
import app.organicmaps.base.BaseMwmFragmentActivity;
import app.organicmaps.downloader.MapManagerHelper;
@@ -438,11 +437,4 @@ public class DownloadResourcesLegacyActivity extends BaseMwmFragmentActivity
.setOnDismissListener(dialog -> mAlertDialog = null)
.show();
}
@Override
@StyleRes
public int getThemeResourceId(@NonNull String theme)
{
return R.style.MwmTheme_DownloadResourcesLegacy;
}
}

View File

@@ -1,210 +0,0 @@
package app.organicmaps;
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.res.ConfigurationHelper;
import app.organicmaps.base.BaseMwmFragment;
import app.organicmaps.sdk.Map;
import app.organicmaps.sdk.MapRenderingListener;
import app.organicmaps.sdk.display.DisplayType;
import app.organicmaps.sdk.util.log.Logger;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
public class MapFragment extends BaseMwmFragment implements View.OnTouchListener, SurfaceHolder.Callback
{
private static final String TAG = MapFragment.class.getSimpleName();
@NonNull
private final Map mMap = new Map(DisplayType.Device);
public void updateCompassOffset(int offsetX, int offsetY)
{
mMap.updateCompassOffset(requireContext(), offsetX, offsetY, true);
}
public void updateBottomWidgetsOffset(int offsetX, int offsetY)
{
mMap.updateBottomWidgetsOffset(requireContext(), offsetX, offsetY);
}
public void updateMyPositionRoutingOffset(int offsetY)
{
mMap.updateMyPositionRoutingOffset(offsetY);
}
public void destroySurface(boolean activityIsChangingConfigurations)
{
mMap.onSurfaceDestroyed(activityIsChangingConfigurations, isAdded());
}
public boolean isContextCreated()
{
return mMap.isContextCreated();
}
@Override
public void surfaceCreated(@NonNull SurfaceHolder surfaceHolder)
{
Logger.d(TAG);
int densityDpi;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
densityDpi = ConfigurationHelper.getDensityDpi(requireContext().getResources());
else
densityDpi = getDensityDpiOld();
mMap.onSurfaceCreated(requireContext(), surfaceHolder.getSurface(), surfaceHolder.getSurfaceFrame(), densityDpi);
}
@Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int format, int width, int height)
{
Logger.d(TAG);
mMap.onSurfaceChanged(requireContext(), surfaceHolder.getSurface(), surfaceHolder.getSurfaceFrame(),
surfaceHolder.isCreating());
}
@Override
public void surfaceDestroyed(@NonNull SurfaceHolder surfaceHolder)
{
Logger.d(TAG);
mMap.onSurfaceDestroyed(requireActivity().isChangingConfigurations(), true);
}
@Override
public void onAttach(Context context)
{
Logger.d(TAG);
super.onAttach(context);
mMap.setLocationHelper(MwmApplication.from(requireContext()).getLocationHelper());
mMap.setMapRenderingListener((MapRenderingListener) context);
mMap.setCallbackUnsupported(this::reportUnsupported);
}
@Override
public void onDetach()
{
Logger.d(TAG);
super.onDetach();
mMap.setMapRenderingListener(null);
mMap.setCallbackUnsupported(null);
}
@Override
public void onCreate(Bundle b)
{
Logger.d(TAG);
super.onCreate(b);
setRetainInstance(true);
boolean launchByDeepLink = false;
Bundle args = getArguments();
if (args != null)
launchByDeepLink = args.getBoolean(Map.ARG_LAUNCH_BY_DEEP_LINK);
mMap.onCreate(launchByDeepLink);
}
@Override
public void onStart()
{
Logger.d(TAG);
super.onStart();
mMap.onStart();
}
@Override
public void onStop()
{
Logger.d(TAG);
super.onStop();
mMap.onStop();
}
@Override
public void onPause()
{
Logger.d(TAG);
super.onPause();
mMap.onPause(requireContext());
}
@Override
public void onResume()
{
Logger.d(TAG);
super.onResume();
mMap.onResume();
}
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState)
{
Logger.d(TAG);
final View view = inflater.inflate(R.layout.fragment_map, container, false);
final SurfaceView mSurfaceView = view.findViewById(R.id.map_surfaceview);
mSurfaceView.getHolder().addCallback(this);
return view;
}
@Override
public boolean onTouch(View view, MotionEvent event)
{
int action = event.getActionMasked();
int pointerIndex = event.getActionIndex();
switch (action)
{
case MotionEvent.ACTION_POINTER_UP -> action = Map.NATIVE_ACTION_UP;
case MotionEvent.ACTION_UP ->
{
action = Map.NATIVE_ACTION_UP;
pointerIndex = 0;
}
case MotionEvent.ACTION_POINTER_DOWN -> action = Map.NATIVE_ACTION_DOWN;
case MotionEvent.ACTION_DOWN ->
{
action = Map.NATIVE_ACTION_DOWN;
pointerIndex = 0;
}
case MotionEvent.ACTION_MOVE ->
{
action = Map.NATIVE_ACTION_MOVE;
pointerIndex = Map.INVALID_POINTER_MASK;
}
case MotionEvent.ACTION_CANCEL -> action = Map.NATIVE_ACTION_CANCEL;
}
Map.onTouch(action, event, pointerIndex);
return true;
}
public void notifyOnSurfaceDestroyed(@NonNull Runnable task)
{
mMap.onSurfaceDestroyed(false, true);
task.run();
}
private void reportUnsupported()
{
new MaterialAlertDialogBuilder(requireContext(), R.style.MwmTheme_AlertDialog)
.setMessage(R.string.unsupported_phone)
.setCancelable(false)
.setPositiveButton(R.string.close, (dlg, which) -> requireActivity().moveTaskToBack(true))
.show();
}
private int getDensityDpiOld()
{
final DisplayMetrics metrics = new DisplayMetrics();
requireActivity().getWindowManager().getDefaultDisplay().getMetrics(metrics);
return metrics.densityDpi;
}
}

View File

@@ -42,7 +42,6 @@ import androidx.annotation.CallSuper;
import androidx.annotation.Keep;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StyleRes;
import androidx.annotation.UiThread;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
@@ -85,6 +84,7 @@ import app.organicmaps.routing.RoutingPlanInplaceController;
import app.organicmaps.sdk.ChoosePositionMode;
import app.organicmaps.sdk.Framework;
import app.organicmaps.sdk.Map;
import app.organicmaps.sdk.MapController;
import app.organicmaps.sdk.MapRenderingListener;
import app.organicmaps.sdk.PlacePageActivationListener;
import app.organicmaps.sdk.Router;
@@ -138,10 +138,10 @@ import java.util.ArrayList;
import java.util.Objects;
public class MwmActivity extends BaseMwmFragmentActivity
implements PlacePageActivationListener, View.OnTouchListener, MapRenderingListener, RoutingController.Container,
LocationListener, SensorListener, LocationState.ModeChangeListener,
RoutingPlanInplaceController.RoutingPlanListener, RoutingBottomMenuListener,
BookmarkManager.BookmarksLoadingListener, FloatingSearchToolbarController.SearchToolbarListener,
implements PlacePageActivationListener, MapRenderingListener, RoutingController.Container, LocationListener,
SensorListener, LocationState.ModeChangeListener, RoutingPlanInplaceController.RoutingPlanListener,
RoutingBottomMenuListener, BookmarkManager.BookmarksLoadingListener,
FloatingSearchToolbarController.SearchToolbarListener,
MenuBottomSheetFragment.MenuBottomSheetInterfaceWithHeader,
PlacePageController.PlacePageRouteSettingsListener, MapButtonsController.MapButtonClickListener,
DisplayChangedListener
@@ -171,8 +171,9 @@ public class MwmActivity extends BaseMwmFragmentActivity
private static final String POWER_SAVE_DISCLAIMER_SHOWN = "POWER_SAVE_DISCLAIMER_SHOWN";
@Nullable
private MapFragment mMapFragment;
@SuppressWarnings("NotNullFieldNotInitialized")
@NonNull
private MapController mMapController;
private View mPointChooser;
private MaterialToolbar mPointChooserToolbar;
@@ -245,7 +246,7 @@ public class MwmActivity extends BaseMwmFragmentActivity
ManageRouteBottomSheet mManageRouteBottomSheet;
private boolean mRemoveDisplayListener = true;
private int mLastUiMode = Configuration.UI_MODE_TYPE_UNDEFINED;
private static int mLastUiMode = Configuration.UI_MODE_TYPE_UNDEFINED;
public interface LeftAnimationTrackListener
{
@@ -455,25 +456,12 @@ public class MwmActivity extends BaseMwmFragmentActivity
}
}
@Override
@StyleRes
protected int getThemeResourceId(@NonNull String theme)
{
if (Config.UiTheme.isDefault(theme))
return R.style.MwmTheme_MainActivity;
if (Config.UiTheme.isNight(theme))
return R.style.MwmTheme_Night_MainActivity;
return super.getThemeResourceId(theme);
}
@Override
public void onDisplayChangedToCar(@NonNull Runnable onTaskFinishedCallback)
{
mRemoveDisplayListener = false;
startActivity(new Intent(this, MapPlaceholderActivity.class));
Objects.requireNonNull(mMapFragment).notifyOnSurfaceDestroyed(onTaskFinishedCallback);
mMapController.setOnDestroyListener(onTaskFinishedCallback);
finish();
}
@@ -482,13 +470,15 @@ public class MwmActivity extends BaseMwmFragmentActivity
{
super.onConfigurationChanged(newConfig);
final int newUiMode = newConfig.uiMode & Configuration.UI_MODE_TYPE_MASK;
final boolean newUiModeIsCarConnected = newUiMode == Configuration.UI_MODE_TYPE_CAR;
final boolean newUiModeIsCarDisconnected =
mLastUiMode == Configuration.UI_MODE_TYPE_CAR && newUiMode == Configuration.UI_MODE_TYPE_NORMAL;
mLastUiMode = newUiMode;
final int newType = newConfig.uiMode & Configuration.UI_MODE_TYPE_MASK;
final int oldType = mLastUiMode & Configuration.UI_MODE_TYPE_MASK;
if (newUiModeIsCarConnected || newUiModeIsCarDisconnected)
mLastUiMode = newConfig.uiMode;
final boolean carModeChanged =
newType != oldType && (newType == Configuration.UI_MODE_TYPE_CAR || oldType == Configuration.UI_MODE_TYPE_CAR);
if (carModeChanged)
return;
makeNavigationBarTransparentInLightMode();
@@ -550,7 +540,7 @@ public class MwmActivity extends BaseMwmFragmentActivity
updateViewsInsets();
if (getIntent().getBooleanExtra(EXTRA_UPDATE_THEME, false))
ThemeSwitcher.INSTANCE.restart(isMapRendererActive());
ThemeSwitcher.INSTANCE.restart(mMapController.isRenderingActive());
/*
* onRenderingInitializationFinished() hook is not called when MwmActivity is recreated with the already
@@ -618,7 +608,10 @@ public class MwmActivity extends BaseMwmFragmentActivity
private void initViews(boolean isLaunchByDeeplink)
{
initMap(isLaunchByDeeplink);
mMapController = new MapController(findViewById(R.id.map), MwmApplication.from(this).getLocationHelper(), this,
this::reportUnsupported, isLaunchByDeeplink);
getLifecycle().addObserver(mMapController);
initNavigationButtons();
if (!mIsTabletLayout)
@@ -732,16 +725,7 @@ public class MwmActivity extends BaseMwmFragmentActivity
private void showPositionChooser(ChoosePositionMode mode, boolean isBusiness, boolean applyPosition)
{
closeFloatingToolbarsAndPanels(false);
if (mMapFragment != null)
{
final View mapView = mMapFragment.getView();
if (mapView != null)
{
int width = mapView.getWidth();
int height = mapView.getHeight();
Framework.nativeSetVisibleRect(0, 0, width, height);
}
}
UiUtils.show(mPointChooser);
mMapButtonsViewModel.setButtonsHidden(true);
ChoosePositionMode.set(mode, isBusiness, applyPosition);
@@ -760,29 +744,6 @@ public class MwmActivity extends BaseMwmFragmentActivity
finish();
}
private void initMap(boolean isLaunchByDeepLink)
{
final FragmentManager manager = getSupportFragmentManager();
mMapFragment = (MapFragment) manager.findFragmentByTag(MapFragment.class.getName());
if (mMapFragment == null)
{
Bundle args = new Bundle();
args.putBoolean(Map.ARG_LAUNCH_BY_DEEP_LINK, isLaunchByDeepLink);
final FragmentFactory factory = manager.getFragmentFactory();
mMapFragment = (MapFragment) factory.instantiate(getClassLoader(), MapFragment.class.getName());
mMapFragment.setArguments(args);
manager.beginTransaction()
.replace(R.id.map_fragment_container, mMapFragment, MapFragment.class.getName())
.commit();
}
View container = findViewById(R.id.map_fragment_container);
if (container != null)
{
container.setOnTouchListener(this);
}
}
private void initNavigationButtons()
{
prepareNavigationButtons();
@@ -1211,7 +1172,7 @@ public class MwmActivity extends BaseMwmFragmentActivity
{
setIntent(intent);
super.onNewIntent(intent);
if (isMapRendererActive())
if (mMapController.isRenderingActive())
processIntent();
if (intent.getAction() != null && intent.getAction().equals(TrackRecordingService.STOP_TRACK_RECORDING))
{
@@ -1221,17 +1182,12 @@ public class MwmActivity extends BaseMwmFragmentActivity
}
}
private boolean isMapRendererActive()
{
return mMapFragment != null && Map.isEngineCreated() && mMapFragment.isContextCreated();
}
@CallSuper
@Override
protected void onResume()
{
super.onResume();
ThemeSwitcher.INSTANCE.restart(isMapRendererActive());
ThemeSwitcher.INSTANCE.restart(mMapController.isRenderingActive());
refreshSearchToolbar();
setFullscreen(isFullscreen());
makeNavigationBarTransparentInLightMode();
@@ -1249,15 +1205,6 @@ public class MwmActivity extends BaseMwmFragmentActivity
MwmApplication.from(this).getSensorHelper().addListener(this);
}
@Override
public void recreate()
{
// Explicitly destroy surface before activity recreation.
if (mMapFragment != null)
mMapFragment.destroySurface(true);
super.recreate();
}
@Override
protected void onResumeFragments()
{
@@ -1459,12 +1406,6 @@ public class MwmActivity extends BaseMwmFragmentActivity
return super.onGenericMotionEvent(event);
}
@Override
public boolean onTouch(View view, MotionEvent event)
{
return mMapFragment != null && mMapFragment.onTouch(view, event);
}
public void customOnNavigateUp()
{
if (removeCurrentFragment(true))
@@ -1480,10 +1421,7 @@ public class MwmActivity extends BaseMwmFragmentActivity
void updateCompassOffset(int offsetY, int offsetX)
{
if (mMapFragment == null || !mMapFragment.isAdded())
return;
mMapFragment.updateCompassOffset(offsetX, offsetY);
mMapController.updateCompassOffset(offsetX, offsetY);
final double north = MwmApplication.from(this).getSensorHelper().getSavedNorth();
if (!Double.isNaN(north))
@@ -1502,9 +1440,6 @@ public class MwmActivity extends BaseMwmFragmentActivity
public void updateBottomWidgetsOffset(int offsetX)
{
if (mMapFragment == null || !mMapFragment.isAdded())
return;
int offsetY = mNavBarHeight;
final Float bottomButtonHeight = mMapButtonsViewModel.getBottomButtonsHeight().getValue();
if (bottomButtonHeight != null)
@@ -1519,8 +1454,8 @@ public class MwmActivity extends BaseMwmFragmentActivity
if (mDisplayManager.isDeviceDisplayUsed())
{
mMapFragment.updateBottomWidgetsOffset(offsetX, offsetY);
mMapFragment.updateMyPositionRoutingOffset(offsetY);
mMapController.updateBottomWidgetsOffset(offsetX, offsetY);
mMapController.updateMyPositionRoutingOffset(offsetY);
}
}
@@ -1738,7 +1673,7 @@ public class MwmActivity extends BaseMwmFragmentActivity
public void onNavigationCancelled()
{
closeFloatingToolbarsAndPanels(true);
ThemeSwitcher.INSTANCE.restart(isMapRendererActive());
ThemeSwitcher.INSTANCE.restart(mMapController.isRenderingActive());
if (mRoutingPlanInplaceController == null)
return;
@@ -1754,7 +1689,7 @@ public class MwmActivity extends BaseMwmFragmentActivity
public void onNavigationStarted()
{
closeFloatingToolbarsAndPanels(true);
ThemeSwitcher.INSTANCE.restart(isMapRendererActive());
ThemeSwitcher.INSTANCE.restart(mMapController.isRenderingActive());
mMapButtonsViewModel.setLayoutMode(MapButtonsController.LayoutMode.navigation);
refreshLightStatusBar();
@@ -1790,7 +1725,7 @@ public class MwmActivity extends BaseMwmFragmentActivity
public void onResetToPlanningState()
{
closeFloatingToolbarsAndPanels(true);
ThemeSwitcher.INSTANCE.restart(isMapRendererActive());
ThemeSwitcher.INSTANCE.restart(mMapController.isRenderingActive());
NavigationService.stopService(this);
mMapButtonsViewModel.setSearchOption(null);
mMapButtonsViewModel.setLayoutMode(MapButtonsController.LayoutMode.planning);
@@ -2619,4 +2554,13 @@ public class MwmActivity extends BaseMwmFragmentActivity
window.setNavigationBarContrastEnforced(false);
}
}
private void reportUnsupported()
{
new MaterialAlertDialogBuilder(this, R.style.MwmTheme_AlertDialog)
.setMessage(R.string.unsupported_phone)
.setCancelable(false)
.setPositiveButton(R.string.close, (dlg, which) -> this.moveTaskToBack(true))
.show();
}
}

View File

@@ -56,15 +56,6 @@ public class SplashActivity extends AppCompatActivity
protected void onCreate(@Nullable Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
final String theme = Config.UiTheme.getCurrent();
if (Config.UiTheme.isDefault(theme))
setTheme(R.style.MwmTheme_Splash);
else if (Config.UiTheme.isNight(theme))
setTheme(R.style.MwmTheme_Night_Splash);
else
throw new IllegalArgumentException("Attempt to apply unsupported theme: " + theme);
UiThread.cancelDelayedTasks(mInitCoreDelayedTask);
setContentView(R.layout.activity_splash);

View File

@@ -8,14 +8,13 @@ import androidx.annotation.Nullable;
import androidx.annotation.StyleRes;
import androidx.fragment.app.DialogFragment;
import app.organicmaps.R;
import app.organicmaps.util.ThemeUtils;
public class BaseMwmDialogFragment extends DialogFragment
{
@StyleRes
protected final int getFullscreenTheme()
{
return ThemeUtils.isNightTheme() ? getFullscreenDarkTheme() : getFullscreenLightTheme();
return R.style.MwmTheme_DialogFragment_Fullscreen;
}
protected int getStyle()
@@ -23,7 +22,8 @@ public class BaseMwmDialogFragment extends DialogFragment
return STYLE_NORMAL;
}
protected @StyleRes int getCustomTheme()
@StyleRes
protected int getCustomTheme()
{
return 0;
}
@@ -40,18 +40,6 @@ public class BaseMwmDialogFragment extends DialogFragment
setStyle(style, theme);
}
@StyleRes
protected int getFullscreenLightTheme()
{
return R.style.MwmTheme_DialogFragment_Fullscreen;
}
@StyleRes
protected int getFullscreenDarkTheme()
{
return R.style.MwmTheme_DialogFragment_Fullscreen_Night;
}
@NonNull
protected Application getAppContextOrThrow()
{

View File

@@ -11,7 +11,6 @@ import androidx.activity.SystemBarStyle;
import androidx.annotation.CallSuper;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StyleRes;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentFactory;
@@ -20,7 +19,6 @@ import app.organicmaps.MwmApplication;
import app.organicmaps.R;
import app.organicmaps.SplashActivity;
import app.organicmaps.sdk.util.Config;
import app.organicmaps.sdk.util.concurrency.UiThread;
import app.organicmaps.sdk.util.log.Logger;
import app.organicmaps.util.RtlUtils;
import com.google.android.material.appbar.MaterialToolbar;
@@ -32,24 +30,9 @@ public abstract class BaseMwmFragmentActivity extends AppCompatActivity
private boolean mSafeCreated;
@NonNull
private String mThemeName;
@StyleRes
protected int getThemeResourceId(@NonNull String theme)
{
if (Config.UiTheme.isDefault(theme))
return R.style.MwmTheme;
if (Config.UiTheme.isNight(theme))
return R.style.MwmTheme_Night;
throw new IllegalArgumentException("Attempt to apply unsupported theme: " + theme);
}
/**
* Shows splash screen and initializes the core in case when it was not initialized.
*
* <p>
* Do not override this method!
* Use {@link #onSafeCreate(Bundle savedInstanceState)}
*/
@@ -58,8 +41,6 @@ public abstract class BaseMwmFragmentActivity extends AppCompatActivity
protected final void onCreate(@Nullable Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
mThemeName = Config.UiTheme.getCurrent();
setTheme(getThemeResourceId(mThemeName));
EdgeToEdge.enable(this, SystemBarStyle.dark(Color.TRANSPARENT));
RtlUtils.manageRtl(this);
if (!MwmApplication.from(this).getOrganicMaps().arePlatformAndCoreInitialized())
@@ -113,18 +94,6 @@ public abstract class BaseMwmFragmentActivity extends AppCompatActivity
mSafeCreated = false;
}
@CallSuper
@Override
public void onPostResume()
{
super.onPostResume();
if (!mThemeName.equals(Config.UiTheme.getCurrent()))
{
// Workaround described in https://code.google.com/p/android/issues/detail?id=93731
UiThread.runLater(this::recreate);
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item)
{

View File

@@ -6,13 +6,11 @@ import android.os.Bundle;
import androidx.annotation.CallSuper;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StyleRes;
import androidx.fragment.app.Fragment;
import app.organicmaps.R;
import app.organicmaps.base.BaseToolbarActivity;
import app.organicmaps.sdk.bookmarks.data.BookmarkCategory;
import app.organicmaps.sdk.bookmarks.data.BookmarkManager;
import app.organicmaps.util.ThemeUtils;
public class BookmarkCategoriesActivity extends BaseToolbarActivity
{
@@ -38,13 +36,6 @@ public class BookmarkCategoriesActivity extends BaseToolbarActivity
super.onPause();
}
@Override
@StyleRes
public int getThemeResourceId(@NonNull String theme)
{
return ThemeUtils.getWindowBgThemeResourceId(theme);
}
@Override
protected Class<? extends Fragment> getFragmentClass()
{

View File

@@ -54,7 +54,6 @@ public class BookmarkCategorySettingsFragment extends BaseMwmToolbarFragment
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState)
{
View root = inflater.inflate(R.layout.fragment_bookmark_category_settings, container, false);
setHasOptionsMenu(true);
initViews(root);
return root;
}
@@ -85,7 +84,7 @@ public class BookmarkCategorySettingsFragment extends BaseMwmToolbarFragment
});
mEditDescView = root.findViewById(R.id.edit_description);
mEditDescView.setText(mCategory.getDescription());
mSaveView = root.findViewById(R.id.done);
mSaveView = root.findViewById(R.id.save);
mSaveView.setOnClickListener(v -> onEditDoneClicked());
}

View File

@@ -5,13 +5,11 @@ import android.os.Bundle;
import androidx.activity.result.ActivityResultLauncher;
import androidx.annotation.CallSuper;
import androidx.annotation.NonNull;
import androidx.annotation.StyleRes;
import androidx.fragment.app.Fragment;
import app.organicmaps.R;
import app.organicmaps.base.BaseToolbarActivity;
import app.organicmaps.sdk.bookmarks.data.BookmarkCategory;
import app.organicmaps.sdk.bookmarks.data.BookmarkManager;
import app.organicmaps.util.ThemeUtils;
public class BookmarkListActivity extends BaseToolbarActivity
{
@@ -37,13 +35,6 @@ public class BookmarkListActivity extends BaseToolbarActivity
super.onPause();
}
@Override
@StyleRes
public int getThemeResourceId(@NonNull String theme)
{
return ThemeUtils.getCardBgThemeResourceId(theme);
}
@Override
protected Class<? extends Fragment> getFragmentClass()
{

View File

@@ -12,9 +12,12 @@ import androidx.lifecycle.DefaultLifecycleObserver;
import androidx.lifecycle.LifecycleOwner;
import app.organicmaps.MwmApplication;
import app.organicmaps.R;
import app.organicmaps.car.renderer.Renderer;
import app.organicmaps.car.renderer.RendererFactory;
import app.organicmaps.car.screens.ErrorScreen;
import app.organicmaps.car.screens.MapPlaceholderScreen;
import app.organicmaps.car.screens.MapScreen;
import app.organicmaps.car.screens.NavigationScreen;
import app.organicmaps.car.screens.PlaceScreen;
import app.organicmaps.car.screens.base.BaseMapScreen;
import app.organicmaps.car.screens.download.DownloadMapsScreen;
@@ -49,8 +52,9 @@ public final class CarAppSession extends Session implements DefaultLifecycleObse
@Nullable
private final SessionInfo mSessionInfo;
@SuppressWarnings("NotNullFieldNotInitialized")
@NonNull
private final SurfaceRenderer mSurfaceRenderer;
private Renderer mSurfaceRenderer;
@NonNull
private final ScreenManager mScreenManager;
@SuppressWarnings("NotNullFieldNotInitialized")
@@ -67,7 +71,6 @@ public final class CarAppSession extends Session implements DefaultLifecycleObse
{
getLifecycle().addObserver(this);
mSessionInfo = sessionInfo;
mSurfaceRenderer = new SurfaceRenderer(getCarContext(), getLifecycle());
mScreenManager = getCarContext().getCarService(ScreenManager.class);
mCurrentCountryChangedListener = new CurrentCountryChangedListener();
}
@@ -114,6 +117,8 @@ public final class CarAppSession extends Session implements DefaultLifecycleObse
mSensorsManager = new CarSensorsManager(getCarContext());
mDisplayManager = MwmApplication.from(getCarContext()).getDisplayManager();
mDisplayManager.addListener(DisplayType.Car, this);
mSurfaceRenderer = RendererFactory.create(getCarContext(), mDisplayManager,
MwmApplication.from(getCarContext()).getLocationHelper(), this);
init();
}
@@ -281,7 +286,19 @@ public final class CarAppSession extends Session implements DefaultLifecycleObse
private void restoreRoute()
{
final RoutingController routingController = RoutingController.get();
if (routingController.isPlanning() || routingController.isNavigating() || routingController.hasSavedRoute())
final boolean isNavigating = routingController.isNavigating();
final boolean hasNavigatingScreen = hasNavigationScreenInStack();
if (!isNavigating && hasNavigatingScreen)
mScreenManager.popToRoot();
if (isNavigating && routingController.getLastRouterType() == PlaceScreen.ROUTER && hasNavigatingScreen)
{
mScreenManager.popTo(NavigationScreen.MARKER);
return;
}
if (routingController.isPlanning() || isNavigating || routingController.hasSavedRoute())
{
final PlaceScreen placeScreen = new PlaceScreen.Builder(getCarContext(), mSurfaceRenderer)
.setMapObject(routingController.getEndPoint())
@@ -290,4 +307,14 @@ public final class CarAppSession extends Session implements DefaultLifecycleObse
mScreenManager.push(placeScreen);
}
}
private boolean hasNavigationScreenInStack()
{
for (final Screen screen : mScreenManager.getScreenStack())
{
if (NavigationScreen.MARKER.equals(screen.getMarker()))
return true;
}
return false;
}
}

View File

@@ -1,244 +0,0 @@
package app.organicmaps.car;
import static app.organicmaps.sdk.display.DisplayType.Car;
import android.graphics.Rect;
import android.view.Surface;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.car.app.AppManager;
import androidx.car.app.CarContext;
import androidx.car.app.CarToast;
import androidx.car.app.SurfaceCallback;
import androidx.car.app.SurfaceContainer;
import androidx.lifecycle.DefaultLifecycleObserver;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleOwner;
import app.organicmaps.MwmApplication;
import app.organicmaps.R;
import app.organicmaps.sdk.Framework;
import app.organicmaps.sdk.Map;
import app.organicmaps.sdk.MapRenderingListener;
import app.organicmaps.sdk.settings.UnitLocale;
import app.organicmaps.sdk.util.concurrency.UiThread;
import app.organicmaps.sdk.util.log.Logger;
public class SurfaceRenderer implements DefaultLifecycleObserver, SurfaceCallback, MapRenderingListener
{
private static final String TAG = SurfaceRenderer.class.getSimpleName();
@NonNull
private final CarContext mCarContext;
@NonNull
private final Map mMap = new Map(Car);
@NonNull
private Rect mVisibleArea = new Rect();
@Nullable
private Surface mSurface = null;
private boolean mIsRunning;
public SurfaceRenderer(@NonNull CarContext carContext, @NonNull Lifecycle lifecycle)
{
Logger.d(TAG, "SurfaceRenderer()");
mCarContext = carContext;
mIsRunning = true;
lifecycle.addObserver(this);
mMap.setMapRenderingListener(this);
}
@Override
public void onSurfaceAvailable(@NonNull SurfaceContainer surfaceContainer)
{
Logger.d(TAG, "Surface available " + surfaceContainer);
if (mSurface != null)
mSurface.release();
mSurface = surfaceContainer.getSurface();
mMap.setLocationHelper(MwmApplication.from(mCarContext).getLocationHelper());
mMap.onSurfaceCreated(mCarContext, mSurface,
new Rect(0, 0, surfaceContainer.getWidth(), surfaceContainer.getHeight()),
surfaceContainer.getDpi());
mMap.updateBottomWidgetsOffset(mCarContext, -1, -1);
}
@Override
public void onVisibleAreaChanged(@NonNull Rect visibleArea)
{
Logger.d(TAG, "Visible area changed. visibleArea: " + visibleArea);
mVisibleArea = visibleArea;
if (!mVisibleArea.isEmpty())
Framework.nativeSetVisibleRect(mVisibleArea.left, mVisibleArea.top, mVisibleArea.right, mVisibleArea.bottom);
}
@Override
public void onStableAreaChanged(@NonNull Rect stableArea)
{
Logger.d(TAG, "Stable area changed. stableArea: " + stableArea);
if (!stableArea.isEmpty())
Framework.nativeSetVisibleRect(stableArea.left, stableArea.top, stableArea.right, stableArea.bottom);
else if (!mVisibleArea.isEmpty())
Framework.nativeSetVisibleRect(mVisibleArea.left, mVisibleArea.top, mVisibleArea.right, mVisibleArea.bottom);
}
@Override
public void onSurfaceDestroyed(@NonNull SurfaceContainer surfaceContainer)
{
Logger.d(TAG, "Surface destroyed");
if (mSurface != null)
{
mSurface.release();
mSurface = null;
}
mMap.onSurfaceDestroyed(false, true);
}
@Override
public void onCreate(@NonNull LifecycleOwner owner)
{
Logger.d(TAG);
mCarContext.getCarService(AppManager.class).setSurfaceCallback(this);
mMap.onCreate(false);
}
@Override
public void onStart(@NonNull LifecycleOwner owner)
{
Logger.d(TAG);
mMap.onStart();
mMap.setCallbackUnsupported(this::reportUnsupported);
}
@Override
public void onResume(@NonNull LifecycleOwner owner)
{
Logger.d(TAG);
mMap.onResume();
if (MwmApplication.from(mCarContext).getDisplayManager().isCarDisplayUsed())
UiThread.runLater(() -> mMap.updateMyPositionRoutingOffset(0));
}
@Override
public void onPause(@NonNull LifecycleOwner owner)
{
Logger.d(TAG);
mMap.onPause(mCarContext);
}
@Override
public void onStop(@NonNull LifecycleOwner owner)
{
Logger.d(TAG);
mMap.onStop();
mMap.setCallbackUnsupported(null);
}
@Override
public void onScroll(float distanceX, float distanceY)
{
Logger.d(TAG, "distanceX: " + distanceX + ", distanceY: " + distanceY);
mMap.onScroll(distanceX, distanceY);
}
@Override
public void onFling(float velocityX, float velocityY)
{
Logger.d(TAG, "velocityX: " + velocityX + ", velocityY: " + velocityY);
}
public void onZoomIn()
{
Map.zoomIn();
}
public void onZoomOut()
{
Map.zoomOut();
}
@Override
public void onScale(float focusX, float focusY, float scaleFactor)
{
Logger.d(TAG, "focusX: " + focusX + ", focusY: " + focusY + ", scaleFactor: " + scaleFactor);
float x = focusX;
float y = focusY;
if (!mVisibleArea.isEmpty())
{
// If a focal point value is negative, use the center point of the visible area.
if (x < 0)
x = mVisibleArea.centerX();
if (y < 0)
y = mVisibleArea.centerY();
}
final boolean animated = Float.compare(scaleFactor, 2f) == 0;
Map.onScale(scaleFactor, x, y, animated);
}
@Override
public void onClick(float x, float y)
{
Logger.d(TAG, "x: " + x + ", y: " + y);
Map.onClick(x, y);
}
public void disable()
{
if (!mIsRunning)
{
Logger.d(TAG, "Already disabled");
return;
}
mCarContext.getCarService(AppManager.class).setSurfaceCallback(null);
mMap.onSurfaceDestroyed(false, true);
mMap.onStop();
mMap.setCallbackUnsupported(null);
mMap.setMapRenderingListener(null);
mIsRunning = false;
}
public void enable()
{
if (mIsRunning)
{
Logger.d(TAG, "Already enabled");
return;
}
mCarContext.getCarService(AppManager.class).setSurfaceCallback(this);
mMap.onStart();
mMap.setCallbackUnsupported(this::reportUnsupported);
mMap.setMapRenderingListener(this);
UiThread.runLater(() -> mMap.updateMyPositionRoutingOffset(0));
mIsRunning = true;
}
public boolean isRenderingActive()
{
return mIsRunning;
}
private void reportUnsupported()
{
String message = mCarContext.getString(R.string.unsupported_phone);
Logger.e(TAG, message);
CarToast.makeText(mCarContext, message, CarToast.LENGTH_LONG).show();
}
@Override
public void onRenderingCreated()
{
UnitLocale.initializeCurrentUnits();
}
}

View File

@@ -0,0 +1,130 @@
package app.organicmaps.car.renderer;
import androidx.annotation.CallSuper;
import androidx.annotation.NonNull;
import androidx.car.app.AppManager;
import androidx.car.app.CarContext;
import androidx.car.app.SurfaceCallback;
import androidx.lifecycle.DefaultLifecycleObserver;
import androidx.lifecycle.LifecycleOwner;
import app.organicmaps.sdk.Map;
import app.organicmaps.sdk.MapRenderingListener;
import app.organicmaps.sdk.display.DisplayManager;
import app.organicmaps.sdk.location.LocationHelper;
import app.organicmaps.sdk.settings.UnitLocale;
import app.organicmaps.sdk.util.log.Logger;
public abstract class Renderer implements DefaultLifecycleObserver
{
@NonNull
private final String TAG;
private SurfaceCallback mSurfaceCallback;
private boolean mIsRunning;
@NonNull
protected final CarContext mCarContext;
@NonNull
protected final DisplayManager mDisplayManager;
@NonNull
protected final LocationHelper mLocationHelper;
@NonNull
protected final LifecycleOwner mLifecycleOwner;
@NonNull
private final MapRenderingListener mMapRenderingListener = new MapRenderingListener() {
@Override
public void onRenderingCreated()
{
UnitLocale.initializeCurrentUnits();
}
};
public Renderer(@NonNull CarContext carContext, @NonNull DisplayManager displayManager,
@NonNull LocationHelper locationHelper, @NonNull LifecycleOwner lifecycleOwner)
{
TAG = getClass().getSimpleName();
Logger.d(TAG, "SurfaceRenderer()");
mIsRunning = true;
mCarContext = carContext;
mDisplayManager = displayManager;
mLocationHelper = locationHelper;
mLifecycleOwner = lifecycleOwner;
mLifecycleOwner.getLifecycle().addObserver(this);
}
protected void setSurfaceCallback(@NonNull SurfaceCallback surfaceCallback)
{
mSurfaceCallback = surfaceCallback;
}
public boolean isRenderingActive()
{
return mIsRunning;
}
protected MapRenderingListener getMapRenderingListener()
{
return mMapRenderingListener;
}
@CallSuper
@Override
public void onCreate(@NonNull LifecycleOwner owner)
{
Logger.d(TAG);
if (mSurfaceCallback == null)
throw new IllegalStateException("SurfaceCallback must be set before onCreate()");
mCarContext.getCarService(AppManager.class).setSurfaceCallback(mSurfaceCallback);
}
@CallSuper
@Override
public void onDestroy(@NonNull LifecycleOwner owner)
{
Logger.d(TAG);
mCarContext.getCarService(AppManager.class).setSurfaceCallback(null);
}
@CallSuper
public void enable()
{
if (isRenderingActive())
{
Logger.d(TAG, "Already enabled");
return;
}
if (mSurfaceCallback == null)
throw new IllegalStateException("SurfaceCallback must be set before enable()");
mCarContext.getCarService(AppManager.class).setSurfaceCallback(mSurfaceCallback);
mIsRunning = true;
}
@CallSuper
public void disable()
{
if (!isRenderingActive())
{
Logger.d(TAG, "Already disabled");
return;
}
mCarContext.getCarService(AppManager.class).setSurfaceCallback(null);
mIsRunning = false;
}
public void onZoomIn()
{
Map.zoomIn();
}
public void onZoomOut()
{
Map.zoomOut();
}
}

View File

@@ -0,0 +1,20 @@
package app.organicmaps.car.renderer;
import androidx.annotation.NonNull;
import androidx.car.app.CarContext;
import androidx.lifecycle.LifecycleOwner;
import app.organicmaps.sdk.display.DisplayManager;
import app.organicmaps.sdk.location.LocationHelper;
public final class RendererFactory
{
@NonNull
public static Renderer create(@NonNull CarContext carContext, @NonNull DisplayManager displayManager,
@NonNull LocationHelper locationHelper, @NonNull LifecycleOwner lifecycleOwner)
{
if (android.os.Build.VERSION.SDK_INT >= 23)
return new SurfaceRenderer(carContext, displayManager, locationHelper, lifecycleOwner);
else
return new SurfaceRendererLegacy(carContext, displayManager, locationHelper, lifecycleOwner);
}
}

View File

@@ -0,0 +1,100 @@
package app.organicmaps.car.renderer;
import android.app.Presentation;
import android.hardware.display.DisplayManager;
import android.hardware.display.VirtualDisplay;
import android.view.SurfaceHolder;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.widget.FrameLayout;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.car.app.CarContext;
import androidx.car.app.SurfaceContainer;
import app.organicmaps.sdk.MapController;
import app.organicmaps.sdk.util.log.Logger;
@RequiresApi(23)
class SurfaceCallback extends SurfaceCallbackBase
{
private static final String TAG = SurfaceCallback.class.getSimpleName();
private static final String VIRTUAL_DISPLAY_NAME = "OM_Android_Auto_Display";
@NonNull
private final MapController mMapController;
private VirtualDisplay mVirtualDisplay;
private Presentation mPresentation;
public SurfaceCallback(@NonNull CarContext carContext, @NonNull MapController mapController)
{
super(carContext);
mMapController = mapController;
mMapController.getView().getHolder().addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height)
{
mMapController.updateMyPositionRoutingOffset(0);
}
@Override
public void surfaceCreated(@NonNull SurfaceHolder holder)
{
mMapController.updateMyPositionRoutingOffset(0);
}
@Override
public void surfaceDestroyed(@NonNull SurfaceHolder holder)
{}
});
}
@Override
public void onSurfaceAvailable(@NonNull SurfaceContainer surfaceContainer)
{
Logger.d(TAG, "Surface available " + surfaceContainer);
mVirtualDisplay =
mCarContext.getSystemService(DisplayManager.class)
.createVirtualDisplay(VIRTUAL_DISPLAY_NAME, surfaceContainer.getWidth(), surfaceContainer.getHeight(),
surfaceContainer.getDpi(), surfaceContainer.getSurface(), 0);
mPresentation = new Presentation(mCarContext, mVirtualDisplay.getDisplay());
mPresentation.setContentView(prepareViewForPresentation(mMapController.getView()));
mPresentation.show();
}
@Override
public void onSurfaceDestroyed(@NonNull SurfaceContainer surfaceContainer)
{
Logger.d(TAG, "Surface destroyed");
mPresentation.dismiss();
mVirtualDisplay.release();
}
void stopPresenting()
{
if (mPresentation != null)
mPresentation.dismiss();
}
void startPresenting()
{
if (mPresentation != null)
mPresentation.show();
}
@NonNull
private View prepareViewForPresentation(@NonNull View view)
{
final ViewParent parent = view.getParent();
if (parent instanceof ViewGroup)
((ViewGroup) parent).removeView(view);
final FrameLayout container = new FrameLayout(mCarContext);
container.addView(
view, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
return container;
}
}

View File

@@ -0,0 +1,96 @@
package app.organicmaps.car.renderer;
import android.graphics.Rect;
import androidx.annotation.NonNull;
import androidx.car.app.CarContext;
import androidx.car.app.SurfaceCallback;
import app.organicmaps.sdk.Framework;
import app.organicmaps.sdk.Map;
import app.organicmaps.sdk.util.concurrency.UiThread;
import app.organicmaps.sdk.util.log.Logger;
abstract class SurfaceCallbackBase implements SurfaceCallback
{
@NonNull
private final String TAG;
@NonNull
protected final CarContext mCarContext;
@NonNull
protected Rect mVisibleArea = new Rect();
public SurfaceCallbackBase(@NonNull CarContext carContext)
{
TAG = getClass().getSimpleName();
mCarContext = carContext;
}
@Override
public void onVisibleAreaChanged(@NonNull Rect visibleArea)
{
Logger.d(TAG, "Visible area changed. visibleArea: " + visibleArea);
mVisibleArea = visibleArea;
if (!mVisibleArea.isEmpty())
UiThread.runLater(()
-> Framework.nativeSetVisibleRect(mVisibleArea.left, mVisibleArea.top, mVisibleArea.right,
mVisibleArea.bottom));
}
@Override
public void onStableAreaChanged(@NonNull Rect stableArea)
{
Logger.d(TAG, "Stable area changed. stableArea: " + stableArea);
if (!mVisibleArea.isEmpty())
UiThread.runLater(()
-> Framework.nativeSetVisibleRect(mVisibleArea.left, mVisibleArea.top, mVisibleArea.right,
mVisibleArea.bottom));
else if (!stableArea.isEmpty())
UiThread.runLater(
() -> Framework.nativeSetVisibleRect(stableArea.left, stableArea.top, stableArea.right, stableArea.bottom));
}
@Override
public void onScroll(float distanceX, float distanceY)
{
Logger.d(TAG, "distanceX: " + distanceX + ", distanceY: " + distanceY);
Map.onScroll(distanceX, distanceY);
}
@Override
public void onFling(float velocityX, float velocityY)
{
Logger.d(TAG, "velocityX: " + velocityX + ", velocityY: " + velocityY);
// TODO: Implement fling in the native code.
}
@Override
public void onScale(float focusX, float focusY, float scaleFactor)
{
Logger.d(TAG, "focusX: " + focusX + ", focusY: " + focusY + ", scaleFactor: " + scaleFactor);
float x = focusX;
float y = focusY;
if (!mVisibleArea.isEmpty())
{
// If a focal point value is negative, use the center point of the visible area.
if (x < 0)
x = mVisibleArea.centerX();
if (y < 0)
y = mVisibleArea.centerY();
}
final boolean animated = Float.compare(scaleFactor, 2f) == 0;
Map.onScale(scaleFactor, x, y, animated);
}
@Override
public void onClick(float x, float y)
{
Logger.d(TAG, "x: " + x + ", y: " + y);
Map.onClick(x, y);
}
}

View File

@@ -0,0 +1,60 @@
package app.organicmaps.car.renderer;
import android.graphics.Rect;
import android.view.Surface;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.car.app.CarContext;
import androidx.car.app.SurfaceContainer;
import app.organicmaps.sdk.Map;
import app.organicmaps.sdk.location.LocationHelper;
import app.organicmaps.sdk.util.log.Logger;
class SurfaceCallbackLegacy extends SurfaceCallbackBase
{
private static final String TAG = SurfaceCallbackLegacy.class.getSimpleName();
@NonNull
private final Map mMap;
@NonNull
private final LocationHelper mLocationHelper;
@Nullable
private Surface mSurface = null;
public SurfaceCallbackLegacy(@NonNull CarContext carContext, @NonNull Map map, @NonNull LocationHelper locationHelper)
{
super(carContext);
mMap = map;
mLocationHelper = locationHelper;
}
@Override
public void onSurfaceAvailable(@NonNull SurfaceContainer surfaceContainer)
{
Logger.d(TAG, "Surface available " + surfaceContainer);
if (mSurface != null)
mSurface.release();
mSurface = surfaceContainer.getSurface();
mMap.setLocationHelper(mLocationHelper);
mMap.onSurfaceCreated(mCarContext, mSurface,
new Rect(0, 0, surfaceContainer.getWidth(), surfaceContainer.getHeight()),
surfaceContainer.getDpi());
mMap.updateBottomWidgetsOffset(mCarContext, -1, -1);
}
@Override
public void onSurfaceDestroyed(@NonNull SurfaceContainer surfaceContainer)
{
Logger.d(TAG, "Surface destroyed");
if (mSurface != null)
{
mSurface.release();
mSurface = null;
}
mMap.onSurfaceDestroyed(false);
}
}

View File

@@ -0,0 +1,53 @@
package app.organicmaps.car.renderer;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.car.app.CarContext;
import androidx.lifecycle.LifecycleOwner;
import app.organicmaps.sdk.MapController;
import app.organicmaps.sdk.MapView;
import app.organicmaps.sdk.display.DisplayManager;
import app.organicmaps.sdk.display.DisplayType;
import app.organicmaps.sdk.location.LocationHelper;
@RequiresApi(23)
class SurfaceRenderer extends Renderer
{
@NonNull
private final MapController mMapController;
@NonNull
private final SurfaceCallback mSurfaceCallback;
public SurfaceRenderer(@NonNull CarContext carContext, @NonNull DisplayManager displayManager,
@NonNull LocationHelper locationHelper, @NonNull LifecycleOwner lifecycleOwner)
{
super(carContext, displayManager, locationHelper, lifecycleOwner);
mMapController = new MapController(new MapView(carContext, DisplayType.Car), locationHelper,
getMapRenderingListener(), null, false);
mLifecycleOwner.getLifecycle().addObserver(mMapController);
mSurfaceCallback = new SurfaceCallback(mCarContext, mMapController);
setSurfaceCallback(mSurfaceCallback);
}
@Override
public void enable()
{
super.enable();
mMapController.onStart(mLifecycleOwner);
mMapController.updateMyPositionRoutingOffset(0);
mSurfaceCallback.startPresenting();
}
@Override
public void disable()
{
super.disable();
mMapController.onPause(mLifecycleOwner);
mSurfaceCallback.stopPresenting();
mMapController.onStop(mLifecycleOwner);
}
}

View File

@@ -0,0 +1,90 @@
package app.organicmaps.car.renderer;
import static app.organicmaps.sdk.display.DisplayType.Car;
import androidx.annotation.NonNull;
import androidx.car.app.CarContext;
import androidx.lifecycle.LifecycleOwner;
import app.organicmaps.sdk.Map;
import app.organicmaps.sdk.display.DisplayManager;
import app.organicmaps.sdk.location.LocationHelper;
import app.organicmaps.sdk.util.log.Logger;
class SurfaceRendererLegacy extends Renderer
{
private static final String TAG = SurfaceRendererLegacy.class.getSimpleName();
@NonNull
private final Map mMap = new Map(Car);
public SurfaceRendererLegacy(@NonNull CarContext carContext, @NonNull DisplayManager displayManager,
@NonNull LocationHelper locationHelper, @NonNull LifecycleOwner lifecycleOwner)
{
super(carContext, displayManager, locationHelper, lifecycleOwner);
setSurfaceCallback(new SurfaceCallbackLegacy(mCarContext, mMap, mLocationHelper));
mMap.setMapRenderingListener(getMapRenderingListener());
}
@Override
public void onCreate(@NonNull LifecycleOwner owner)
{
super.onCreate(owner);
mMap.onCreate(false);
}
@Override
public void onStart(@NonNull LifecycleOwner owner)
{
Logger.d(TAG);
if (mDisplayManager.isCarDisplayUsed())
mMap.onStart();
}
@Override
public void onResume(@NonNull LifecycleOwner owner)
{
Logger.d(TAG);
if (mDisplayManager.isCarDisplayUsed())
{
mMap.onResume();
mMap.updateMyPositionRoutingOffset(0);
}
}
@Override
public void onPause(@NonNull LifecycleOwner owner)
{
Logger.d(TAG);
if (mDisplayManager.isCarDisplayUsed())
mMap.onPause();
}
@Override
public void onStop(@NonNull LifecycleOwner owner)
{
Logger.d(TAG);
if (mDisplayManager.isCarDisplayUsed())
mMap.onStop();
}
@Override
public void enable()
{
super.enable();
mMap.onStart();
mMap.setMapRenderingListener(getMapRenderingListener());
mMap.updateMyPositionRoutingOffset(0);
}
@Override
public void disable()
{
super.disable();
mMap.onPause();
mMap.onSurfaceDestroyed(false);
mMap.onStop();
mMap.setMapRenderingListener(null);
}
}

View File

@@ -15,30 +15,29 @@ import androidx.car.app.model.Template;
import androidx.car.app.navigation.model.MapWithContentTemplate;
import androidx.core.graphics.drawable.IconCompat;
import app.organicmaps.R;
import app.organicmaps.car.SurfaceRenderer;
import app.organicmaps.car.renderer.Renderer;
import app.organicmaps.car.screens.base.BaseMapScreen;
import app.organicmaps.car.screens.search.SearchOnMapScreen;
import app.organicmaps.car.util.ThemeUtils;
import app.organicmaps.car.util.UiHelpers;
import java.util.Arrays;
import java.util.List;
public class CategoriesScreen extends BaseMapScreen
{
private record CategoryData(@StringRes int nameResId, @DrawableRes int iconResId, @DrawableRes int iconNightResId) {}
private record CategoryData(@StringRes int nameResId, @DrawableRes int iconResId) {}
private static final List<CategoryData> CATEGORIES = Arrays.asList(
new CategoryData(R.string.category_fuel, R.drawable.ic_category_fuel, R.drawable.ic_category_fuel_night),
new CategoryData(R.string.category_parking, R.drawable.ic_category_parking, R.drawable.ic_category_parking_night),
new CategoryData(R.string.category_eat, R.drawable.ic_category_eat, R.drawable.ic_category_eat_night),
new CategoryData(R.string.category_food, R.drawable.ic_category_food, R.drawable.ic_category_food_night),
new CategoryData(R.string.category_hotel, R.drawable.ic_category_hotel, R.drawable.ic_category_hotel_night),
new CategoryData(R.string.category_toilet, R.drawable.ic_category_toilet, R.drawable.ic_category_toilet_night),
new CategoryData(R.string.category_rv, R.drawable.ic_category_rv, R.drawable.ic_category_rv_night));
private static final List<CategoryData> CATEGORIES =
Arrays.asList(new CategoryData(R.string.category_fuel, R.drawable.ic_category_fuel),
new CategoryData(R.string.category_parking, R.drawable.ic_category_parking),
new CategoryData(R.string.category_eat, R.drawable.ic_category_eat),
new CategoryData(R.string.category_food, R.drawable.ic_category_food),
new CategoryData(R.string.category_hotel, R.drawable.ic_category_hotel),
new CategoryData(R.string.category_toilet, R.drawable.ic_category_toilet),
new CategoryData(R.string.category_rv, R.drawable.ic_category_rv));
private final int MAX_CATEGORIES_SIZE;
public CategoriesScreen(@NonNull CarContext carContext, @NonNull SurfaceRenderer surfaceRenderer)
public CategoriesScreen(@NonNull CarContext carContext, @NonNull Renderer surfaceRenderer)
{
super(carContext, surfaceRenderer);
final ConstraintManager constraintManager = getCarContext().getCarService(ConstraintManager.class);
@@ -47,7 +46,7 @@ public class CategoriesScreen extends BaseMapScreen
@NonNull
@Override
public Template onGetTemplate()
protected Template onGetTemplateImpl()
{
final MapWithContentTemplate.Builder builder = new MapWithContentTemplate.Builder();
builder.setMapController(UiHelpers.createMapController(getCarContext(), getSurfaceRenderer()));
@@ -67,7 +66,6 @@ public class CategoriesScreen extends BaseMapScreen
@NonNull
private GridTemplate createCategoriesListTemplate()
{
final boolean isNightMode = ThemeUtils.isNightMode(getCarContext());
final ItemList.Builder builder = new ItemList.Builder();
final int categoriesSize = Math.min(CATEGORIES.size(), MAX_CATEGORIES_SIZE);
for (int i = 0; i < categoriesSize; ++i)
@@ -75,7 +73,7 @@ public class CategoriesScreen extends BaseMapScreen
final GridItem.Builder itemBuilder = new GridItem.Builder();
final String title = getCarContext().getString(CATEGORIES.get(i).nameResId);
@DrawableRes
final int iconResId = isNightMode ? CATEGORIES.get(i).iconNightResId : CATEGORIES.get(i).iconResId;
final int iconResId = CATEGORIES.get(i).iconResId;
itemBuilder.setTitle(title);
itemBuilder.setImage(new CarIcon.Builder(IconCompat.createWithResource(getCarContext(), iconResId)).build());

View File

@@ -43,7 +43,7 @@ public class ErrorScreen extends BaseScreen implements UserActionRequired
@NonNull
@Override
public Template onGetTemplate()
protected Template onGetTemplateImpl()
{
final MessageTemplate.Builder builder = new MessageTemplate.Builder(getCarContext().getString(mErrorMessage));

View File

@@ -9,20 +9,20 @@ import androidx.car.app.model.Template;
import androidx.car.app.navigation.model.NavigationTemplate;
import androidx.core.graphics.drawable.IconCompat;
import app.organicmaps.R;
import app.organicmaps.car.SurfaceRenderer;
import app.organicmaps.car.renderer.Renderer;
import app.organicmaps.car.screens.base.BaseMapScreen;
import app.organicmaps.car.util.UiHelpers;
public class FreeDriveScreen extends BaseMapScreen
{
public FreeDriveScreen(@NonNull CarContext carContext, @NonNull SurfaceRenderer surfaceRenderer)
public FreeDriveScreen(@NonNull CarContext carContext, @NonNull Renderer surfaceRenderer)
{
super(carContext, surfaceRenderer);
}
@NonNull
@Override
public Template onGetTemplate()
protected Template onGetTemplateImpl()
{
final NavigationTemplate.Builder builder = new NavigationTemplate.Builder();
builder.setMapActionStrip(UiHelpers.createMapActionStrip(getCarContext(), getSurfaceRenderer()));

View File

@@ -22,7 +22,7 @@ public class MapPlaceholderScreen extends BaseScreen
@NonNull
@Override
public Template onGetTemplate()
protected Template onGetTemplateImpl()
{
final MessageTemplate.Builder builder =
new MessageTemplate.Builder(getCarContext().getString(R.string.car_used_on_the_phone_screen));

View File

@@ -14,7 +14,7 @@ import androidx.car.app.model.Template;
import androidx.car.app.navigation.model.MapWithContentTemplate;
import androidx.core.graphics.drawable.IconCompat;
import app.organicmaps.R;
import app.organicmaps.car.SurfaceRenderer;
import app.organicmaps.car.renderer.Renderer;
import app.organicmaps.car.screens.base.BaseMapScreen;
import app.organicmaps.car.screens.bookmarks.BookmarkCategoriesScreen;
import app.organicmaps.car.screens.search.SearchScreen;
@@ -24,14 +24,14 @@ import app.organicmaps.car.util.UiHelpers;
public class MapScreen extends BaseMapScreen
{
public MapScreen(@NonNull CarContext carContext, @NonNull SurfaceRenderer surfaceRenderer)
public MapScreen(@NonNull CarContext carContext, @NonNull Renderer surfaceRenderer)
{
super(carContext, surfaceRenderer);
}
@NonNull
@Override
public Template onGetTemplate()
protected Template onGetTemplateImpl()
{
SuggestionsHelpers.updateSuggestions(getCarContext());

View File

@@ -20,7 +20,7 @@ import androidx.lifecycle.LifecycleOwner;
import app.organicmaps.MwmApplication;
import app.organicmaps.R;
import app.organicmaps.car.CarAppService;
import app.organicmaps.car.SurfaceRenderer;
import app.organicmaps.car.renderer.Renderer;
import app.organicmaps.car.screens.base.BaseMapScreen;
import app.organicmaps.car.screens.settings.DrivingOptionsScreen;
import app.organicmaps.car.util.Colors;
@@ -70,7 +70,7 @@ public class NavigationScreen extends BaseMapScreen implements RoutingController
@NonNull
@Override
public Template onGetTemplate()
protected Template onGetTemplateImpl()
{
final NavigationTemplate.Builder builder = new NavigationTemplate.Builder();
builder.setBackgroundColor(Colors.NAVIGATION_TEMPLATE_BACKGROUND);
@@ -104,7 +104,11 @@ public class NavigationScreen extends BaseMapScreen implements RoutingController
public void onAutoDriveEnabled()
{
Logger.i(TAG);
final JunctionInfo[] points = Framework.nativeGetRouteJunctionPoints();
/// @todo Pass maxDistM from RouteSimulationProvider?
/// Result speed between points will be in range (25, 50] km/h (for 1 second update interval).
final double kMaxDistM = 13.9; // 13.9 m/s == 50 km/h
final JunctionInfo[] points = Framework.nativeGetRouteJunctionPoints(kMaxDistM);
if (points == null)
{
Logger.e(TAG, "Navigation has not started yet");
@@ -121,6 +125,8 @@ public class NavigationScreen extends BaseMapScreen implements RoutingController
if (!mNavigationCancelled)
CarToast.makeText(getCarContext(), getCarContext().getString(R.string.trip_finished), CarToast.LENGTH_LONG)
.show();
NavigationService.stopService(getCarContext());
ThemeUtils.update(getCarContext());
finish();
getScreenManager().popToRoot();
}
@@ -128,7 +134,7 @@ public class NavigationScreen extends BaseMapScreen implements RoutingController
@Override
public void onCreate(@NonNull LifecycleOwner owner)
{
Logger.d(TAG);
super.onCreate(owner);
mRoutingController.attach(this);
ThemeUtils.update(getCarContext());
mNavigationManager.setNavigationManagerCallback(this);
@@ -144,20 +150,19 @@ public class NavigationScreen extends BaseMapScreen implements RoutingController
@Override
public void onResume(@NonNull LifecycleOwner owner)
{
Logger.d(TAG);
super.onResume(owner);
mRoutingController.attach(this);
}
@Override
public void onDestroy(@NonNull LifecycleOwner owner)
{
NavigationService.stopService(getCarContext());
super.onDestroy(owner);
MwmApplication.from(getCarContext()).getLocationHelper().removeListener(mLocationListener);
if (mRoutingController.isNavigating())
mRoutingController.onSaveState();
mRoutingController.detach();
ThemeUtils.update(getCarContext());
mNavigationManager.navigationEnded();
mNavigationManager.clearNavigationManagerCallback();
}
@@ -257,9 +262,9 @@ public class NavigationScreen extends BaseMapScreen implements RoutingController
@NonNull
private final CarContext mCarContext;
@NonNull
private final SurfaceRenderer mSurfaceRenderer;
private final Renderer mSurfaceRenderer;
public Builder(@NonNull final CarContext carContext, @NonNull final SurfaceRenderer surfaceRenderer)
public Builder(@NonNull final CarContext carContext, @NonNull final Renderer surfaceRenderer)
{
mCarContext = carContext;
mSurfaceRenderer = surfaceRenderer;

View File

@@ -26,7 +26,7 @@ import androidx.core.graphics.drawable.IconCompat;
import androidx.lifecycle.LifecycleOwner;
import app.organicmaps.MwmApplication;
import app.organicmaps.R;
import app.organicmaps.car.SurfaceRenderer;
import app.organicmaps.car.renderer.Renderer;
import app.organicmaps.car.screens.base.BaseMapScreen;
import app.organicmaps.car.screens.download.DownloadMapsScreenBuilder;
import app.organicmaps.car.screens.settings.DrivingOptionsScreen;
@@ -46,7 +46,7 @@ import java.util.Objects;
public class PlaceScreen extends BaseMapScreen implements OnBackPressedCallback.Callback, RoutingController.Container
{
private static final Router ROUTER = Router.Vehicle;
public static final Router ROUTER = Router.Vehicle;
@Nullable
private MapObject mMapObject;
@@ -68,7 +68,7 @@ public class PlaceScreen extends BaseMapScreen implements OnBackPressedCallback.
@NonNull
@Override
public Template onGetTemplate()
protected Template onGetTemplateImpl()
{
final MapWithContentTemplate.Builder builder = new MapWithContentTemplate.Builder();
builder.setMapController(UiHelpers.createMapController(getCarContext(), getSurfaceRenderer()));
@@ -80,6 +80,7 @@ public class PlaceScreen extends BaseMapScreen implements OnBackPressedCallback.
@Override
public void onCreate(@NonNull LifecycleOwner owner)
{
super.onCreate(owner);
mRoutingController.restore();
if (mRoutingController.isNavigating() && mRoutingController.getLastRouterType() == ROUTER)
{
@@ -112,12 +113,14 @@ public class PlaceScreen extends BaseMapScreen implements OnBackPressedCallback.
@Override
public void onResume(@NonNull LifecycleOwner owner)
{
super.onResume(owner);
mRoutingController.attach(this);
}
@Override
public void onDestroy(@NonNull LifecycleOwner owner)
{
super.onDestroy(owner);
if (mRoutingController.isPlanning())
mRoutingController.onSaveState();
if (!mRoutingController.isNavigating())
@@ -340,11 +343,11 @@ public class PlaceScreen extends BaseMapScreen implements OnBackPressedCallback.
@NonNull
private final CarContext mCarContext;
@NonNull
private final SurfaceRenderer mSurfaceRenderer;
private final Renderer mSurfaceRenderer;
@Nullable
private MapObject mMapObject;
public Builder(@NonNull final CarContext carContext, @NonNull final SurfaceRenderer surfaceRenderer)
public Builder(@NonNull final CarContext carContext, @NonNull final Renderer surfaceRenderer)
{
mCarContext = carContext;
mSurfaceRenderer = surfaceRenderer;

View File

@@ -2,21 +2,21 @@ package app.organicmaps.car.screens.base;
import androidx.annotation.NonNull;
import androidx.car.app.CarContext;
import app.organicmaps.car.SurfaceRenderer;
import app.organicmaps.car.renderer.Renderer;
public abstract class BaseMapScreen extends BaseScreen
{
@NonNull
private final SurfaceRenderer mSurfaceRenderer;
private final Renderer mSurfaceRenderer;
public BaseMapScreen(@NonNull CarContext carContext, @NonNull SurfaceRenderer surfaceRenderer)
public BaseMapScreen(@NonNull CarContext carContext, @NonNull Renderer surfaceRenderer)
{
super(carContext);
mSurfaceRenderer = surfaceRenderer;
}
@NonNull
protected SurfaceRenderer getSurfaceRenderer()
protected Renderer getSurfaceRenderer()
{
return mSurfaceRenderer;
}

View File

@@ -1,15 +1,71 @@
package app.organicmaps.car.screens.base;
import androidx.annotation.CallSuper;
import androidx.annotation.NonNull;
import androidx.car.app.CarContext;
import androidx.car.app.Screen;
import androidx.car.app.model.Template;
import androidx.lifecycle.DefaultLifecycleObserver;
import androidx.lifecycle.LifecycleOwner;
import app.organicmaps.sdk.util.log.Logger;
public abstract class BaseScreen extends Screen implements DefaultLifecycleObserver
{
@NonNull
private final String TAG;
public BaseScreen(@NonNull CarContext carContext)
{
super(carContext);
TAG = getClass().getSimpleName();
getLifecycle().addObserver(this);
}
@NonNull
protected abstract Template onGetTemplateImpl();
@Override
@NonNull
public final Template onGetTemplate()
{
Logger.d(TAG);
return onGetTemplateImpl();
}
@CallSuper
public void onCreate(@NonNull LifecycleOwner owner)
{
Logger.d(TAG);
}
@CallSuper
public void onStart(@NonNull LifecycleOwner owner)
{
Logger.d(TAG);
}
@CallSuper
public void onResume(@NonNull LifecycleOwner owner)
{
Logger.d(TAG);
}
@CallSuper
public void onPause(@NonNull LifecycleOwner owner)
{
Logger.d(TAG);
}
@CallSuper
public void onStop(@NonNull LifecycleOwner owner)
{
Logger.d(TAG);
}
@CallSuper
public void onDestroy(@NonNull LifecycleOwner owner)
{
Logger.d(TAG);
}
}

View File

@@ -11,7 +11,7 @@ import androidx.car.app.model.Row;
import androidx.car.app.model.Template;
import androidx.car.app.navigation.model.MapWithContentTemplate;
import app.organicmaps.R;
import app.organicmaps.car.SurfaceRenderer;
import app.organicmaps.car.renderer.Renderer;
import app.organicmaps.car.screens.base.BaseMapScreen;
import app.organicmaps.car.util.UiHelpers;
import app.organicmaps.sdk.bookmarks.data.BookmarkCategory;
@@ -23,7 +23,7 @@ public class BookmarkCategoriesScreen extends BaseMapScreen
{
private final int MAX_CATEGORIES_SIZE;
public BookmarkCategoriesScreen(@NonNull CarContext carContext, @NonNull SurfaceRenderer surfaceRenderer)
public BookmarkCategoriesScreen(@NonNull CarContext carContext, @NonNull Renderer surfaceRenderer)
{
super(carContext, surfaceRenderer);
final ConstraintManager constraintManager = getCarContext().getCarService(ConstraintManager.class);
@@ -32,7 +32,7 @@ public class BookmarkCategoriesScreen extends BaseMapScreen
@NonNull
@Override
public Template onGetTemplate()
protected Template onGetTemplateImpl()
{
final MapWithContentTemplate.Builder builder = new MapWithContentTemplate.Builder();
builder.setMapController(UiHelpers.createMapController(getCarContext(), getSurfaceRenderer()));

View File

@@ -13,7 +13,7 @@ import androidx.car.app.navigation.model.MapWithContentTemplate;
import androidx.core.graphics.drawable.IconCompat;
import androidx.lifecycle.LifecycleOwner;
import app.organicmaps.R;
import app.organicmaps.car.SurfaceRenderer;
import app.organicmaps.car.renderer.Renderer;
import app.organicmaps.car.screens.base.BaseMapScreen;
import app.organicmaps.car.util.UiHelpers;
import app.organicmaps.sdk.bookmarks.data.BookmarkCategory;
@@ -31,7 +31,7 @@ public class BookmarksScreen extends BaseMapScreen
private boolean mIsOnSortingScreen = false;
public BookmarksScreen(@NonNull CarContext carContext, @NonNull SurfaceRenderer surfaceRenderer,
public BookmarksScreen(@NonNull CarContext carContext, @NonNull Renderer surfaceRenderer,
@NonNull BookmarkCategory bookmarkCategory)
{
super(carContext, surfaceRenderer);
@@ -41,7 +41,7 @@ public class BookmarksScreen extends BaseMapScreen
@NonNull
@Override
public Template onGetTemplate()
protected Template onGetTemplateImpl()
{
final MapWithContentTemplate.Builder builder = new MapWithContentTemplate.Builder();
builder.setMapController(UiHelpers.createMapController(getCarContext(), getSurfaceRenderer()));
@@ -52,6 +52,7 @@ public class BookmarksScreen extends BaseMapScreen
@Override
public void onStop(@NonNull LifecycleOwner owner)
{
super.onStop(owner);
if (!mIsOnSortingScreen)
mBookmarksLoader.cancel();
}

View File

@@ -16,7 +16,7 @@ import androidx.core.graphics.drawable.IconCompat;
import androidx.lifecycle.LifecycleOwner;
import app.organicmaps.MwmApplication;
import app.organicmaps.R;
import app.organicmaps.car.SurfaceRenderer;
import app.organicmaps.car.renderer.Renderer;
import app.organicmaps.car.screens.base.BaseMapScreen;
import app.organicmaps.car.util.UiHelpers;
import app.organicmaps.sdk.bookmarks.data.BookmarkCategory;
@@ -38,7 +38,7 @@ class SortingScreen extends BaseMapScreen
private @BookmarkManager.SortingType int mNewSortingType;
public SortingScreen(@NonNull CarContext carContext, @NonNull SurfaceRenderer surfaceRenderer,
public SortingScreen(@NonNull CarContext carContext, @NonNull Renderer surfaceRenderer,
@NonNull BookmarkCategory bookmarkCategory)
{
super(carContext, surfaceRenderer);
@@ -53,7 +53,7 @@ class SortingScreen extends BaseMapScreen
@NonNull
@Override
public Template onGetTemplate()
protected Template onGetTemplateImpl()
{
final MapWithContentTemplate.Builder builder = new MapWithContentTemplate.Builder();
builder.setMapController(UiHelpers.createMapController(getCarContext(), getSurfaceRenderer()));

View File

@@ -23,6 +23,7 @@ class DownloadMapsForFirstLaunchScreen extends DownloadMapsScreen
@Override
public void onResume(@NonNull LifecycleOwner owner)
{
super.onResume(owner);
// Attempting to streamline initial download by including the current country in the list of missing maps for
// simultaneous retrieval.
final Location location = MwmApplication.from(getCarContext()).getLocationHelper().getSavedLocation();

View File

@@ -34,7 +34,7 @@ public abstract class DownloadMapsScreen extends BaseScreen
@NonNull
@Override
public final Template onGetTemplate()
protected final Template onGetTemplateImpl()
{
final MessageTemplate.Builder builder = new MessageTemplate.Builder(getText(getMapsSize(mMissingMaps)));
final Header.Builder headerBuilder = new Header.Builder();

View File

@@ -102,6 +102,7 @@ class DownloaderScreen extends BaseScreen
@Override
public void onResume(@NonNull LifecycleOwner owner)
{
super.onResume(owner);
if (mSubscriptionSlot == 0)
mSubscriptionSlot = MapManager.nativeSubscribe(mStorageCallback);
for (final var item : mMissingMaps.entrySet())
@@ -114,6 +115,7 @@ class DownloaderScreen extends BaseScreen
@Override
public void onPause(@NonNull LifecycleOwner owner)
{
super.onPause(owner);
if (!mIsDownloadFailed)
cancelMapsDownloading();
if (mSubscriptionSlot != 0)
@@ -125,7 +127,7 @@ class DownloaderScreen extends BaseScreen
@NonNull
@Override
public Template onGetTemplate()
protected Template onGetTemplateImpl()
{
final MessageTemplate.Builder builder = new MessageTemplate.Builder(getText());
builder.setLoading(true);

View File

@@ -37,7 +37,7 @@ public class RequestPermissionsScreenWithApi extends BaseScreen implements UserA
@NonNull
@Override
public Template onGetTemplate()
protected Template onGetTemplateImpl()
{
final MessageTemplate.Builder builder =
new MessageTemplate.Builder(getCarContext().getString(R.string.aa_request_permission_activity_text));
@@ -63,6 +63,7 @@ public class RequestPermissionsScreenWithApi extends BaseScreen implements UserA
@Override
public void onResume(@NonNull LifecycleOwner owner)
{
super.onResume(owner);
// Let's review the permissions once more, as we might enter this function following an ErrorScreen situation
// where the user manually enabled location permissions.
if (LocationUtils.checkFineLocationPermission(getCarContext()))

View File

@@ -47,7 +47,7 @@ public class RequestPermissionsScreenWithNotification extends BaseScreen impleme
@NonNull
@Override
public Template onGetTemplate()
protected Template onGetTemplateImpl()
{
final MessageTemplate.Builder builder =
new MessageTemplate.Builder(getCarContext().getString(R.string.aa_location_permissions_request));
@@ -66,6 +66,7 @@ public class RequestPermissionsScreenWithNotification extends BaseScreen impleme
@RequiresPermission(value = Manifest.permission.POST_NOTIFICATIONS)
public void onStart(@NonNull LifecycleOwner owner)
{
super.onStart(owner);
mIsPermissionCheckEnabled = true;
mBackgroundExecutor.execute(this::checkPermissions);
sendPermissionsRequestNotification();
@@ -74,12 +75,14 @@ public class RequestPermissionsScreenWithNotification extends BaseScreen impleme
@Override
public void onStop(@NonNull LifecycleOwner owner)
{
super.onStop(owner);
mIsPermissionCheckEnabled = false;
}
@Override
public void onDestroy(@NonNull LifecycleOwner owner)
{
super.onDestroy(owner);
NotificationManagerCompat.from(getCarContext()).cancel(NOTIFICATION_ID);
}

View File

@@ -17,7 +17,7 @@ import androidx.core.graphics.drawable.IconCompat;
import androidx.lifecycle.LifecycleOwner;
import app.organicmaps.MwmApplication;
import app.organicmaps.R;
import app.organicmaps.car.SurfaceRenderer;
import app.organicmaps.car.renderer.Renderer;
import app.organicmaps.car.screens.base.BaseMapScreen;
import app.organicmaps.car.util.UiHelpers;
import app.organicmaps.sdk.bookmarks.data.MapObject;
@@ -54,7 +54,7 @@ public class SearchOnMapScreen extends BaseMapScreen implements SearchListener
@NonNull
@Override
public Template onGetTemplate()
protected Template onGetTemplateImpl()
{
final MapWithContentTemplate.Builder builder = new MapWithContentTemplate.Builder();
builder.setMapController(UiHelpers.createMapController(getCarContext(), getSurfaceRenderer()));
@@ -136,12 +136,14 @@ public class SearchOnMapScreen extends BaseMapScreen implements SearchListener
@Override
public void onStart(@NonNull LifecycleOwner owner)
{
super.onStart(owner);
SearchEngine.INSTANCE.addListener(this);
}
@Override
public void onResume(@NonNull LifecycleOwner owner)
{
super.onResume(owner);
SearchEngine.INSTANCE.cancel();
final MapObject location = MwmApplication.from(getCarContext()).getLocationHelper().getMyPosition();
@@ -156,6 +158,7 @@ public class SearchOnMapScreen extends BaseMapScreen implements SearchListener
@Override
public void onStop(@NonNull LifecycleOwner owner)
{
super.onStop(owner);
SearchEngine.INSTANCE.removeListener(this);
SearchEngine.INSTANCE.cancel();
}
@@ -177,7 +180,7 @@ public class SearchOnMapScreen extends BaseMapScreen implements SearchListener
@NonNull
private final CarContext mCarContext;
@NonNull
private final SurfaceRenderer mSurfaceRenderer;
private final Renderer mSurfaceRenderer;
@NonNull
private String mQuery = "";
@@ -185,7 +188,7 @@ public class SearchOnMapScreen extends BaseMapScreen implements SearchListener
private String mLocale;
private boolean mIsCategory;
public Builder(@NonNull CarContext carContext, @NonNull SurfaceRenderer surfaceRenderer)
public Builder(@NonNull CarContext carContext, @NonNull Renderer surfaceRenderer)
{
mCarContext = carContext;
mSurfaceRenderer = surfaceRenderer;

View File

@@ -16,7 +16,7 @@ import androidx.core.graphics.drawable.IconCompat;
import androidx.lifecycle.LifecycleOwner;
import app.organicmaps.MwmApplication;
import app.organicmaps.R;
import app.organicmaps.car.SurfaceRenderer;
import app.organicmaps.car.renderer.Renderer;
import app.organicmaps.car.screens.base.BaseMapScreen;
import app.organicmaps.sdk.bookmarks.data.MapObject;
import app.organicmaps.sdk.search.SearchEngine;
@@ -50,7 +50,7 @@ public class SearchScreen extends BaseMapScreen implements SearchTemplate.Search
@NonNull
@Override
public Template onGetTemplate()
protected Template onGetTemplateImpl()
{
final SearchTemplate.Builder builder = new SearchTemplate.Builder(this);
builder.setHeaderAction(Action.BACK);
@@ -106,12 +106,14 @@ public class SearchScreen extends BaseMapScreen implements SearchTemplate.Search
@Override
public void onStart(@NonNull LifecycleOwner owner)
{
super.onStart(owner);
SearchEngine.INSTANCE.addListener(this);
}
@Override
public void onStop(@NonNull LifecycleOwner owner)
{
super.onStop(owner);
SearchEngine.INSTANCE.removeListener(this);
SearchEngine.INSTANCE.cancel();
}
@@ -209,14 +211,14 @@ public class SearchScreen extends BaseMapScreen implements SearchTemplate.Search
@NonNull
private final CarContext mCarContext;
@NonNull
private final SurfaceRenderer mSurfaceRenderer;
private final Renderer mSurfaceRenderer;
@NonNull
private String mQuery = "";
@NonNull
private String mLocale;
public Builder(@NonNull CarContext carContext, @NonNull SurfaceRenderer surfaceRenderer)
public Builder(@NonNull CarContext carContext, @NonNull Renderer surfaceRenderer)
{
mCarContext = carContext;
mSurfaceRenderer = surfaceRenderer;

View File

@@ -13,7 +13,7 @@ import androidx.car.app.model.Template;
import androidx.car.app.navigation.model.MapWithContentTemplate;
import androidx.lifecycle.LifecycleOwner;
import app.organicmaps.R;
import app.organicmaps.car.SurfaceRenderer;
import app.organicmaps.car.renderer.Renderer;
import app.organicmaps.car.screens.base.BaseMapScreen;
import app.organicmaps.car.util.Toggle;
import app.organicmaps.car.util.UiHelpers;
@@ -31,12 +31,13 @@ public class DrivingOptionsScreen extends BaseMapScreen
private final DrivingOption[] mDrivingOptions = {new DrivingOption(RoadType.Toll, R.string.avoid_tolls),
new DrivingOption(RoadType.Dirty, R.string.avoid_unpaved),
new DrivingOption(RoadType.Ferry, R.string.avoid_ferry),
new DrivingOption(RoadType.Motorway, R.string.avoid_motorways)};
new DrivingOption(RoadType.Motorway, R.string.avoid_motorways),
new DrivingOption(RoadType.Steps, R.string.avoid_steps)};
@NonNull
private final Map<RoadType, Boolean> mInitialDrivingOptionsState = new HashMap<>();
public DrivingOptionsScreen(@NonNull CarContext carContext, @NonNull SurfaceRenderer surfaceRenderer)
public DrivingOptionsScreen(@NonNull CarContext carContext, @NonNull Renderer surfaceRenderer)
{
super(carContext, surfaceRenderer);
@@ -45,7 +46,7 @@ public class DrivingOptionsScreen extends BaseMapScreen
@NonNull
@Override
public Template onGetTemplate()
protected Template onGetTemplateImpl()
{
final MapWithContentTemplate.Builder builder = new MapWithContentTemplate.Builder();
builder.setMapController(UiHelpers.createMapController(getCarContext(), getSurfaceRenderer()));
@@ -56,6 +57,7 @@ public class DrivingOptionsScreen extends BaseMapScreen
@Override
public void onStop(@NonNull LifecycleOwner owner)
{
super.onStop(owner);
for (final DrivingOption drivingOption : mDrivingOptions)
{
if (Boolean.TRUE.equals(mInitialDrivingOptionsState.get(drivingOption.roadType))

View File

@@ -12,7 +12,7 @@ import androidx.car.app.model.Template;
import androidx.car.app.navigation.model.MapWithContentTemplate;
import app.organicmaps.BuildConfig;
import app.organicmaps.R;
import app.organicmaps.car.SurfaceRenderer;
import app.organicmaps.car.renderer.Renderer;
import app.organicmaps.car.screens.base.BaseMapScreen;
import app.organicmaps.car.util.UiHelpers;
import app.organicmaps.sdk.Framework;
@@ -20,14 +20,14 @@ import app.organicmaps.sdk.util.DateUtils;
public class HelpScreen extends BaseMapScreen
{
public HelpScreen(@NonNull CarContext carContext, @NonNull SurfaceRenderer surfaceRenderer)
public HelpScreen(@NonNull CarContext carContext, @NonNull Renderer surfaceRenderer)
{
super(carContext, surfaceRenderer);
}
@NonNull
@Override
public Template onGetTemplate()
protected Template onGetTemplateImpl()
{
final MapWithContentTemplate.Builder builder = new MapWithContentTemplate.Builder();
builder.setMapController(UiHelpers.createMapController(getCarContext(), getSurfaceRenderer()));

View File

@@ -13,7 +13,7 @@ import androidx.car.app.model.Row;
import androidx.car.app.model.Template;
import androidx.car.app.navigation.model.MapWithContentTemplate;
import app.organicmaps.R;
import app.organicmaps.car.SurfaceRenderer;
import app.organicmaps.car.renderer.Renderer;
import app.organicmaps.car.screens.base.BaseMapScreen;
import app.organicmaps.car.util.ThemeUtils;
import app.organicmaps.car.util.Toggle;
@@ -33,14 +33,14 @@ public class SettingsScreen extends BaseMapScreen
void set(boolean newValue);
}
public SettingsScreen(@NonNull CarContext carContext, @NonNull SurfaceRenderer surfaceRenderer)
public SettingsScreen(@NonNull CarContext carContext, @NonNull Renderer surfaceRenderer)
{
super(carContext, surfaceRenderer);
}
@NonNull
@Override
public Template onGetTemplate()
protected Template onGetTemplateImpl()
{
final MapWithContentTemplate.Builder builder = new MapWithContentTemplate.Builder();
builder.setMapController(UiHelpers.createMapController(getCarContext(), getSurfaceRenderer()));

View File

@@ -12,7 +12,7 @@ import androidx.car.app.model.Template;
import androidx.car.app.navigation.model.MapWithContentTemplate;
import androidx.core.graphics.drawable.IconCompat;
import app.organicmaps.R;
import app.organicmaps.car.SurfaceRenderer;
import app.organicmaps.car.renderer.Renderer;
import app.organicmaps.car.screens.base.BaseMapScreen;
import app.organicmaps.car.util.ThemeUtils;
import app.organicmaps.car.util.UiHelpers;
@@ -24,7 +24,7 @@ public class ThemeScreen extends BaseMapScreen
@NonNull
private final CarIcon mRadioButtonSelectedIcon;
public ThemeScreen(@NonNull CarContext carContext, @NonNull SurfaceRenderer surfaceRenderer)
public ThemeScreen(@NonNull CarContext carContext, @NonNull Renderer surfaceRenderer)
{
super(carContext, surfaceRenderer);
mRadioButtonIcon =
@@ -35,7 +35,7 @@ public class ThemeScreen extends BaseMapScreen
@NonNull
@Override
public Template onGetTemplate()
protected Template onGetTemplateImpl()
{
final MapWithContentTemplate.Builder builder = new MapWithContentTemplate.Builder();
builder.setMapController(UiHelpers.createMapController(getCarContext(), getSurfaceRenderer()));

View File

@@ -12,7 +12,7 @@ import androidx.car.app.notification.CarPendingIntent;
import app.organicmaps.MwmApplication;
import app.organicmaps.api.Const;
import app.organicmaps.car.CarAppService;
import app.organicmaps.car.SurfaceRenderer;
import app.organicmaps.car.renderer.Renderer;
import app.organicmaps.car.screens.NavigationScreen;
import app.organicmaps.car.screens.search.SearchScreen;
import app.organicmaps.sdk.Framework;
@@ -30,7 +30,7 @@ public final class IntentUtils
private static final int SEARCH_IN_VIEWPORT_ZOOM = 16;
public static void processIntent(@NonNull CarContext carContext, @NonNull SurfaceRenderer surfaceRenderer,
public static void processIntent(@NonNull CarContext carContext, @NonNull Renderer surfaceRenderer,
@NonNull Intent intent)
{
final String action = intent.getAction();
@@ -50,8 +50,8 @@ public final class IntentUtils
}
// https://developer.android.com/reference/androidx/car/app/CarContext#startCarApp(android.content.Intent)
private static void processNavigationIntent(@NonNull CarContext carContext, @NonNull SurfaceRenderer surfaceRenderer,
@NonNull Intent intent)
private static void processNavigationIntent(@NonNull CarContext carContext,
@NonNull Renderer surfaceRenderer, @NonNull Intent intent)
{
// TODO (AndrewShkrob): This logic will need to be revised when we introduce support for adding stops during
// navigation or route planning. Skip navigation intents during navigation

View File

@@ -15,7 +15,7 @@ import androidx.car.app.navigation.model.MapController;
import androidx.core.graphics.drawable.IconCompat;
import app.organicmaps.MwmApplication;
import app.organicmaps.R;
import app.organicmaps.car.SurfaceRenderer;
import app.organicmaps.car.renderer.Renderer;
import app.organicmaps.car.screens.base.BaseMapScreen;
import app.organicmaps.car.screens.settings.SettingsScreen;
import app.organicmaps.sdk.Map;
@@ -33,13 +33,14 @@ public final class UiHelpers
{
@NonNull
public static ActionStrip createSettingsActionStrip(@NonNull BaseMapScreen mapScreen,
@NonNull SurfaceRenderer surfaceRenderer)
@NonNull Renderer surfaceRenderer)
{
return new ActionStrip.Builder().addAction(createSettingsAction(mapScreen, surfaceRenderer)).build();
}
@NonNull
public static ActionStrip createMapActionStrip(@NonNull CarContext context, @NonNull SurfaceRenderer surfaceRenderer)
public static ActionStrip createMapActionStrip(@NonNull CarContext context,
@NonNull Renderer surfaceRenderer)
{
final CarIcon iconPlus = new CarIcon.Builder(IconCompat.createWithResource(context, R.drawable.ic_plus)).build();
final CarIcon iconMinus = new CarIcon.Builder(IconCompat.createWithResource(context, R.drawable.ic_minus)).build();
@@ -58,27 +59,30 @@ public final class UiHelpers
}
@NonNull
public static MapController createMapController(@NonNull CarContext context, @NonNull SurfaceRenderer surfaceRenderer)
public static MapController createMapController(@NonNull CarContext context,
@NonNull Renderer surfaceRenderer)
{
return new MapController.Builder().setMapActionStrip(createMapActionStrip(context, surfaceRenderer)).build();
}
@NonNull
public static Action createSettingsAction(@NonNull BaseMapScreen mapScreen, @NonNull SurfaceRenderer surfaceRenderer)
public static Action createSettingsAction(@NonNull BaseMapScreen mapScreen,
@NonNull Renderer surfaceRenderer)
{
return createSettingsAction(mapScreen, surfaceRenderer, null);
}
@NonNull
public static Action createSettingsActionForResult(@NonNull BaseMapScreen mapScreen,
@NonNull SurfaceRenderer surfaceRenderer,
@NonNull Renderer surfaceRenderer,
@NonNull OnScreenResultListener onScreenResultListener)
{
return createSettingsAction(mapScreen, surfaceRenderer, onScreenResultListener);
}
@NonNull
private static Action createSettingsAction(@NonNull BaseMapScreen mapScreen, @NonNull SurfaceRenderer surfaceRenderer,
private static Action createSettingsAction(@NonNull BaseMapScreen mapScreen,
@NonNull Renderer surfaceRenderer,
@Nullable OnScreenResultListener onScreenResultListener)
{
final CarContext context = mapScreen.getCarContext();
@@ -120,7 +124,7 @@ public final class UiHelpers
final Row.Builder builder = new Row.Builder();
builder.setImage(
new CarIcon.Builder(IconCompat.createWithResource(context, R.drawable.ic_operating_hours)).build());
new CarIcon.Builder(IconCompat.createWithResource(context, R.drawable.ic_opening_hours)).build());
if (isEmptyTT)
builder.setTitle(ohStr);

View File

@@ -40,7 +40,6 @@ public class EditTextDialogFragment extends BaseMwmDialogFragment
private String mHint;
private TextInputEditText mEtInput;
private TextInputLayout mEtInputLayout;
private Button mPositiveButton;
private Validator mInputValidator;
private OnTextSaveListener mTextSaveListener;
@@ -117,21 +116,20 @@ public class EditTextDialogFragment extends BaseMwmDialogFragment
AlertDialog editTextDialog = new MaterialAlertDialogBuilder(requireActivity(), R.style.MwmTheme_AlertDialog)
.setView(buildView())
.setNegativeButton(negativeButtonText, null)
.setPositiveButton(positiveButtonText,
(dialog, which) -> {
final String result = mEtInput.getText().toString();
processInput(result);
dismiss();
})
.setPositiveButton(positiveButtonText, null)
.create();
// Wait till alert is shown to get mPositiveButton.
// Set up onClick listener for mPositiveButton.
editTextDialog.setOnShowListener((dialog) -> {
mPositiveButton = editTextDialog.getButton(DialogInterface.BUTTON_POSITIVE);
final FragmentActivity activity = getActivity();
if (activity == null)
return;
this.validateInput(activity, mInitialText);
Button positiveButton = editTextDialog.getButton(DialogInterface.BUTTON_POSITIVE);
positiveButton.setOnClickListener(view -> {
final String result = mEtInput.getText().toString();
if (validateInput(requireActivity(), result)) {
processInput(result);
editTextDialog.dismiss();
}
});
});
// Setup validation on input edit.
@@ -149,14 +147,16 @@ public class EditTextDialogFragment extends BaseMwmDialogFragment
return editTextDialog;
}
private void validateInput(@NonNull FragmentActivity activity, @Nullable String input)
private boolean validateInput(@NonNull FragmentActivity activity, @Nullable String input)
{
if (mPositiveButton != null && mInputValidator != null)
if (mInputValidator != null)
{
final String maybeError = mInputValidator.validate(activity, input);
mPositiveButton.setEnabled(maybeError == null);
mEtInputLayout.getEditText().setError(maybeError);
mEtInputLayout.requestFocus();
return maybeError == null;
}
return false;
}
private void processInput(@Nullable String text)

View File

@@ -1,13 +1,19 @@
package app.organicmaps.editor;
import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Bundle;
import android.text.Editable;
import android.text.InputType;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.text.method.LinkMovementMethod;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AutoCompleteTextView;
import android.widget.GridLayout;
import android.widget.Toast;
import androidx.annotation.CallSuper;
import androidx.annotation.DrawableRes;
import androidx.annotation.IdRes;
@@ -22,6 +28,7 @@ import app.organicmaps.base.BaseMwmFragment;
import app.organicmaps.dialog.EditTextDialogFragment;
import app.organicmaps.editor.data.TimeFormatUtils;
import app.organicmaps.sdk.Framework;
import app.organicmaps.sdk.bookmarks.data.ChargeSocketDescriptor;
import app.organicmaps.sdk.bookmarks.data.Metadata;
import app.organicmaps.sdk.editor.Editor;
import app.organicmaps.sdk.editor.OpeningHours;
@@ -30,26 +37,33 @@ import app.organicmaps.sdk.editor.data.LocalizedStreet;
import app.organicmaps.sdk.editor.data.Timetable;
import app.organicmaps.sdk.util.StringUtils;
import app.organicmaps.sdk.util.Utils;
import app.organicmaps.sdk.util.log.Logger;
import app.organicmaps.util.Graphics;
import app.organicmaps.util.InputUtils;
import app.organicmaps.util.UiUtils;
import com.google.android.material.button.MaterialButton;
import com.google.android.material.card.MaterialCardView;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.imageview.ShapeableImageView;
import com.google.android.material.textfield.TextInputEditText;
import com.google.android.material.textfield.TextInputLayout;
import com.google.android.material.textview.MaterialTextView;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class EditorFragment extends BaseMwmFragment implements View.OnClickListener
{
final static String LAST_INDEX_OF_NAMES_ARRAY = "LastIndexOfNamesArray";
private static final String CHARGE_SOCKETS_TAG = "CHARGE_SOCKETS_TAG";
private MaterialTextView mCategory;
private View mCardName;
private View mCardAddress;
private View mCardChargingStation;
private View mCardDetails;
private View mCardSocialMedia;
private View mCardBuilding;
@@ -130,6 +144,8 @@ public class EditorFragment extends BaseMwmFragment implements View.OnClickListe
private TextInputLayout mInputHouseNumber;
private TextInputLayout mInputBuildingLevels;
private View mChargeSockets;
private View mEmptyOpeningHours;
private MaterialTextView mOpeningHours;
private View mEditOpeningHours;
@@ -206,6 +222,7 @@ public class EditorFragment extends BaseMwmFragment implements View.OnClickListe
mWifi.setChecked(Editor.nativeHasWifi());
// TODO Reimplement this to avoid https://github.com/organicmaps/organicmaps/issues/9049
// mOutdoorSeating.setChecked(Editor.nativeGetSwitchInput(Metadata.MetadataType.FMD_OUTDOOR_SEATING.toInt(),"yes"));
refreshChargeSockets();
refreshOpeningTime();
refreshEditableFields();
refreshResetButton();
@@ -329,6 +346,14 @@ public class EditorFragment extends BaseMwmFragment implements View.OnClickListe
final int[] editableDetails = Editor.nativeGetEditableProperties();
// charge sockets have their own card; check whether we should display it.
boolean hasChargeSockets = false;
for (int type : editableDetails)
{
hasChargeSockets = hasChargeSockets || (type == Metadata.MetadataType.FMD_CHARGE_SOCKETS.toInt());
}
UiUtils.showIf(hasChargeSockets, mCardChargingStation);
setCardVisibility(mCardDetails, mDetailsBlocks, editableDetails);
setCardVisibility(mCardSocialMedia, mSocialMediaBlocks, editableDetails);
}
@@ -351,6 +376,283 @@ public class EditorFragment extends BaseMwmFragment implements View.OnClickListe
UiUtils.showIf(anyBlockElement, card);
}
/**
* Builds a dialog for editing or adding a charge socket.
*
* @param socketIndex The index of the socket to edit, or -1 to add a new socket.
* @param type The current type of the socket (e.g., "type2", "type2_combo").
* @param count The current number of sockets of this type or 0 for new socket.
* @param power The current power output of the socket in kW or 0 for new socket.
* @return A MaterialAlertDialogBuilder instance for the configured dialog.
*/
private MaterialAlertDialogBuilder buildChargeSocketDialog(int socketIndex, String type, int count, double power)
{
LayoutInflater inflater = LayoutInflater.from(getActivity());
View dialogView = inflater.inflate(R.layout.dialog_edit_socket, null);
GridLayout typeBtns = dialogView.findViewById(R.id.edit_socket_type_grid);
typeBtns.removeAllViews();
List<String> SOCKET_TYPES = Arrays.stream(getResources().getStringArray(R.array.charge_socket_types)).toList();
for (String socket : SOCKET_TYPES)
{
MaterialButton btn = (MaterialButton) inflater.inflate(R.layout.button_socket_type, typeBtns, false);
btn.setTag(R.id.socket_type, socket);
// load SVG icon converted into VectorDrawable in res/drawable
@SuppressLint("DiscouragedApi")
int resIconId =
getResources().getIdentifier("ic_charge_socket_" + socket, "drawable", requireContext().getPackageName());
if (resIconId != 0)
{
btn.setIcon(getResources().getDrawable(resIconId));
}
@SuppressLint("DiscouragedApi")
int resTypeId =
getResources().getIdentifier("charge_socket_" + socket, "string", requireContext().getPackageName());
if (resTypeId != 0)
{
btn.setText(getResources().getString(resTypeId));
}
if (socket.equals(type))
{
btn.setChecked(true);
}
typeBtns.addView(btn);
}
// manage the grid of socket type buttons as a single 'radio group'
// (this can not be done with a MaterialButtonToggleGroup because it does
// not support GridLayout)
List<MaterialButton> buttonList = new ArrayList<>();
for (int i = 0; i < typeBtns.getChildCount(); i++)
{
View child = typeBtns.getChildAt(i);
if (child instanceof MaterialButton button)
{
buttonList.add(button);
button.setOnClickListener(view -> {
// deselect all
for (MaterialButton b : buttonList)
{
b.setChecked(false);
}
// select clicked
button.setChecked(true);
});
}
}
TextInputLayout countInputLayout = dialogView.findViewById(R.id.edit_socket_count_layout);
AutoCompleteTextView countView = dialogView.findViewById(R.id.edit_socket_count);
if (count > 0)
{
countView.setText(String.valueOf(count));
}
// Add a TextWatcher to validate on text change
countView.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {}
@Override
public void afterTextChanged(Editable s) {
validatePositiveField(s.toString(), countInputLayout);
}
});
TextInputLayout powerInputLayout = dialogView.findViewById(R.id.edit_socket_power_layout);
AutoCompleteTextView powerView = dialogView.findViewById(R.id.edit_socket_power);
if (power > 0)
{
powerView.setText(String.valueOf(power));
}
// Add a TextWatcher to validate on text change
powerView.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {}
@Override
public void afterTextChanged(Editable s) {
validatePositiveField(s.toString(), powerInputLayout);
}
});
return new MaterialAlertDialogBuilder(requireActivity(), R.style.MwmTheme_AlertDialog)
.setTitle(R.string.editor_socket)
.setView(dialogView)
.setPositiveButton(R.string.save,
(dialog, which) -> {
String socketType = "";
for (MaterialButton b : buttonList)
{
if (b.isChecked())
{
socketType = b.getTag(R.id.socket_type).toString();
break;
}
}
int countValue = 0; // 0 means 'unknown count'
try
{
countValue = Integer.parseInt(countView.getText().toString());
}
catch (NumberFormatException ignored)
{
Logger.w(CHARGE_SOCKETS_TAG, "Invalid count value for socket:" + countView.getText().toString());
}
if (countValue < 0)
{
countValue = 0;
Logger.w(CHARGE_SOCKETS_TAG, "Invalid count value for socket:" + countView.getText().toString());
}
double powerValue = 0; // 0 means 'unknown power'
try
{
powerValue = Double.parseDouble(powerView.getText().toString());
}
catch (NumberFormatException ignored)
{
Logger.w(CHARGE_SOCKETS_TAG, "Invalid power value for socket:" + powerView.getText().toString());
}
if (powerValue < 0)
{
powerValue = 0;
Logger.w(CHARGE_SOCKETS_TAG, "Invalid power value for socket:" + powerView.getText().toString());
}
ChargeSocketDescriptor socket =
new ChargeSocketDescriptor(socketType, countValue, powerValue);
updateChargeSockets(socketIndex, socket);
})
.setNegativeButton(R.string.cancel, (dialog, which) -> { dialog.dismiss(); });
}
// Helper method for validation logic
private boolean validatePositiveField(String text, TextInputLayout layout) {
if (text.isEmpty()) {
layout.setError(null); // No error if empty (assuming 0 is the default)
return true;
}
try {
double value = Double.parseDouble(text);
if (value < 0) {
layout.setError(getString(R.string.error_value_must_be_positive));
return false;
} else {
layout.setError(null);
return true;
}
} catch (NumberFormatException e) {
layout.setError(getString(R.string.error_invalid_number));
return false;
}
}
/**
* Updates the list of charge sockets.
* If socketIndex is >=0, it updates the socket at that index.
* Otherwise, it adds the new socket to the list.
*
* @param socketIndex The index of the socket to update, or -1 to add a new socket.
* @param socket The ChargeSocketDescriptor of the socket to add or update.
*/
private void updateChargeSockets(int socketIndex, ChargeSocketDescriptor socket)
{
ChargeSocketDescriptor[] sockets = Editor.nativeGetChargeSockets();
if (socketIndex >= 0)
{
sockets[socketIndex] = socket;
}
else {
List<ChargeSocketDescriptor> list = new ArrayList<>(Arrays.asList(sockets));
list.add(socket);
sockets = list.toArray(new ChargeSocketDescriptor[0]);
}
Editor.nativeSetChargeSockets(sockets);
refreshChargeSockets();
}
private void refreshChargeSockets()
{
ChargeSocketDescriptor[] sockets = Editor.nativeGetChargeSockets();
LayoutInflater inflater = LayoutInflater.from(requireContext());
GridLayout socketsGrid = mChargeSockets.findViewById(R.id.socket_grid_editor);
socketsGrid.removeAllViews();
for (int i = 0; i < sockets.length; i++)
{
final int currentIndex = i;
ChargeSocketDescriptor socket = sockets[i];
View itemView = inflater.inflate(R.layout.item_charge_socket, socketsGrid, false);
MaterialTextView type = itemView.findViewById(R.id.socket_type);
ShapeableImageView icon = itemView.findViewById(R.id.socket_icon);
MaterialTextView power = itemView.findViewById(R.id.socket_power);
MaterialTextView count = itemView.findViewById(R.id.socket_count);
// load SVG icon converted into VectorDrawable in res/drawable
@SuppressLint("DiscouragedApi")
int resIconId = getResources().getIdentifier("ic_charge_socket_" + socket.type(), "drawable",
requireContext().getPackageName());
if (resIconId != 0)
{
icon.setImageResource(resIconId);
}
@SuppressLint("DiscouragedApi")
int resTypeId =
getResources().getIdentifier("charge_socket_" + socket.type(), "string", requireContext().getPackageName());
if (resTypeId != 0)
{
type.setText(resTypeId);
}
if (socket.power() != 0)
{
DecimalFormat df = new DecimalFormat("#.##");
power.setText(getString(R.string.kw_label, df.format(socket.power())));
}
if (socket.count() != 0)
{
count.setText(getString(R.string.count_label, socket.count()));
}
itemView.setOnClickListener(v -> {
buildChargeSocketDialog(currentIndex, socket.type(), socket.count(), socket.power()).show();
});
socketsGrid.addView(itemView);
}
// add a 'new item' button at the end, to create new sockets
View btnNewItemView = inflater.inflate(R.layout.button_new_item, socketsGrid, false);
btnNewItemView.setOnClickListener(v -> {
buildChargeSocketDialog(-1, "unknown", -1, -1).show();
});
socketsGrid.addView(btnNewItemView);
}
private void refreshOpeningTime()
{
final String openingHours = Editor.nativeGetOpeningHours();
@@ -435,6 +737,7 @@ public class EditorFragment extends BaseMwmFragment implements View.OnClickListe
mCategory = categoryBlock.findViewById(R.id.name);
mCardName = view.findViewById(R.id.cv__name);
mCardAddress = view.findViewById(R.id.cv__address);
mCardChargingStation = view.findViewById(R.id.cv__charging_station);
mCardDetails = view.findViewById(R.id.cv__details);
mCardSocialMedia = view.findViewById(R.id.cv__social_media);
mCardBuilding = view.findViewById(R.id.cv__building);
@@ -507,6 +810,9 @@ public class EditorFragment extends BaseMwmFragment implements View.OnClickListe
View blockOutdoorSeating = view.findViewById(R.id.block_outdoor_seating);
mOutdoorSeating = view.findViewById(R.id.sw__outdoor_seating);
blockOutdoorSeating.setOnClickListener(this);
mChargeSockets = view.findViewById(R.id.block_charge_sockets);
View blockOpeningHours = view.findViewById(R.id.block_opening_hours);
mEditOpeningHours = blockOpeningHours.findViewById(R.id.edit_opening_hours);
mEditOpeningHours.setOnClickListener(this);
@@ -701,7 +1007,7 @@ public class EditorFragment extends BaseMwmFragment implements View.OnClickListe
private void placeDoesntExist()
{
EditTextDialogFragment dialogFragment = EditTextDialogFragment.show(
getString(R.string.editor_place_doesnt_exist), "", getString(R.string.editor_comment_hint),
getString(R.string.editor_place_doesnt_exist), "", getString(R.string.editor_place_doesnt_exist_description),
getString(R.string.editor_report_problem_send_button), getString(R.string.cancel), this,
getDeleteCommentValidator());
dialogFragment.setTextSaveListener(this::commitPlaceDoesntExists);

View File

@@ -13,7 +13,6 @@ import android.widget.TextView;
import android.widget.TimePicker;
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import androidx.annotation.StyleRes;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.content.res.AppCompatResources;
import androidx.fragment.app.FragmentManager;
@@ -21,7 +20,6 @@ import app.organicmaps.R;
import app.organicmaps.base.BaseMwmDialogFragment;
import app.organicmaps.sdk.editor.data.HoursMinutes;
import app.organicmaps.sdk.util.DateUtils;
import app.organicmaps.util.ThemeUtils;
import app.organicmaps.util.Utils;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.tabs.TabLayout;
@@ -79,15 +77,13 @@ public class HoursMinutesPickerFragment extends BaseMwmDialogFragment
// noinspection ConstantConditions
mTabs.getTabAt(mSelectedTab).select();
@StyleRes
final int theme = ThemeUtils.isNightTheme() ? R.style.MwmMain_DialogFragment_TimePicker_Night
: R.style.MwmMain_DialogFragment_TimePicker;
final AlertDialog dialog = new MaterialAlertDialogBuilder(requireActivity(), theme)
.setView(root)
.setNegativeButton(R.string.cancel, null)
.setPositiveButton(R.string.ok, null)
.setCancelable(true)
.create();
final AlertDialog dialog =
new MaterialAlertDialogBuilder(requireActivity(), R.style.MwmMain_DialogFragment_TimePicker)
.setView(root)
.setNegativeButton(R.string.cancel, null)
.setPositiveButton(R.string.ok, null)
.setCancelable(true)
.create();
dialog.setOnShowListener(dialogInterface -> {
mOkButton = dialog.getButton(AlertDialog.BUTTON_POSITIVE);
@@ -141,9 +137,8 @@ public class HoursMinutesPickerFragment extends BaseMwmDialogFragment
mTabs = root.findViewById(R.id.tabs);
MaterialTextView tabView = (MaterialTextView) inflater.inflate(R.layout.tab_timepicker, mTabs, false);
tabView.setText(getResources().getString(R.string.editor_time_from));
final ColorStateList textColor = AppCompatResources.getColorStateList(
requireContext(),
ThemeUtils.isNightTheme() ? R.color.accent_color_selector_night : R.color.accent_color_selector);
final ColorStateList textColor =
AppCompatResources.getColorStateList(requireContext(), R.color.accent_color_selector);
tabView.setTextColor(textColor);
mTabs.addTab(mTabs.newTab().setCustomView(tabView), true);
tabView = (MaterialTextView) inflater.inflate(R.layout.tab_timepicker, mTabs, false);

View File

@@ -22,7 +22,6 @@ import app.organicmaps.util.UiUtils;
import app.organicmaps.util.Utils;
import app.organicmaps.util.WindowInsetUtils;
import app.organicmaps.widget.StackedButtonDialogFragment;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.imageview.ShapeableImageView;
import com.google.android.material.textview.MaterialTextView;
import java.text.NumberFormat;
@@ -135,14 +134,7 @@ public class ProfileFragment extends BaseMwmToolbarFragment
private void logout()
{
new MaterialAlertDialogBuilder(requireContext(), R.style.MwmTheme_AlertDialog)
.setMessage(R.string.osm_log_out_confirmation)
.setPositiveButton(R.string.yes,
(dialog, which) -> {
OsmOAuth.clearAuthorization();
refreshViews();
})
.setNegativeButton(R.string.no, null)
.show();
OsmOAuth.clearAuthorization();
refreshViews();
}
}

View File

@@ -2,6 +2,7 @@ package app.organicmaps.maplayer;
import android.content.Context;
import android.view.View;
import androidx.annotation.AttrRes;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.StringRes;
@@ -13,9 +14,7 @@ import app.organicmaps.util.ThemeUtils;
public class LayerBottomSheetItem
{
@DrawableRes
private final int mEnabledStateDrawableResId;
@DrawableRes
private final int mDisabledStateDrawableResId;
private final int mDrawableResId;
@StringRes
private final int mTitleResId;
@NonNull
@@ -23,12 +22,10 @@ public class LayerBottomSheetItem
@NonNull
private final OnItemClickListener<LayerBottomSheetItem> mItemClickListener;
LayerBottomSheetItem(@DrawableRes int enabledStateDrawableResId, @DrawableRes int disabledStateDrawableResId,
@StringRes int titleResId, @NonNull Mode mode,
LayerBottomSheetItem(@DrawableRes int drawableResId, @StringRes int titleResId, @NonNull Mode mode,
@NonNull OnItemClickListener<LayerBottomSheetItem> itemClickListener)
{
mEnabledStateDrawableResId = enabledStateDrawableResId;
mDisabledStateDrawableResId = disabledStateDrawableResId;
mDrawableResId = drawableResId;
mTitleResId = titleResId;
mMode = mode;
mItemClickListener = itemClickListener;
@@ -37,35 +34,30 @@ public class LayerBottomSheetItem
public static LayerBottomSheetItem create(@NonNull Context mContext, Mode mode,
@NonNull OnItemClickListener<LayerBottomSheetItem> layerItemClickListener)
{
int disabledResource = 0;
int enabledResource = 0;
@DrawableRes
int drawableResId = 0;
@StringRes
int buttonTextResource = R.string.layers_title;
switch (mode)
{
case OUTDOORS:
disabledResource = R.attr.outdoorsMenuDisabled;
enabledResource = R.attr.outdoorsMenuEnabled;
drawableResId = R.drawable.ic_layers_outdoors;
buttonTextResource = R.string.button_layer_outdoor;
break;
case SUBWAY:
disabledResource = R.attr.subwayMenuDisabled;
enabledResource = R.attr.subwayMenuEnabled;
drawableResId = R.drawable.ic_layers_subway;
buttonTextResource = R.string.subway;
break;
case ISOLINES:
disabledResource = R.attr.isoLinesMenuDisabled;
enabledResource = R.attr.isoLinesMenuEnabled;
drawableResId = R.drawable.ic_layers_isoline;
buttonTextResource = R.string.button_layer_isolines;
break;
case TRAFFIC:
disabledResource = R.attr.trafficMenuDisabled;
enabledResource = R.attr.trafficMenuEnabled;
drawableResId = R.drawable.ic_layers_traffic;
buttonTextResource = R.string.button_layer_traffic;
break;
}
int disabled = ThemeUtils.getResource(mContext, disabledResource);
int enabled = ThemeUtils.getResource(mContext, enabledResource);
return new LayerBottomSheetItem(enabled, disabled, buttonTextResource, mode, layerItemClickListener);
return new LayerBottomSheetItem(drawableResId, buttonTextResource, mode, layerItemClickListener);
}
@NonNull
@@ -75,15 +67,9 @@ public class LayerBottomSheetItem
}
@DrawableRes
public int getEnabledStateDrawable()
public int getDrawable()
{
return mEnabledStateDrawableResId;
}
@DrawableRes
public int getDisabledStateDrawable()
{
return mDisabledStateDrawableResId;
return mDrawableResId;
}
@StringRes

View File

@@ -45,7 +45,8 @@ public class LayersAdapter extends RecyclerView.Adapter<LayerHolder>
holder.mTitle.setText(item.getTitle());
boolean isNewLayer = SharedPropertiesUtils.shouldShowNewMarkerForLayerMode(item.getMode());
UiUtils.showIf(isNewLayer, holder.mNewMarker);
holder.mButton.setImageResource(isEnabled ? item.getEnabledStateDrawable() : item.getDisabledStateDrawable());
holder.mButton.setBackgroundResource(item.getDrawable());
holder.mButton.setActivated(isEnabled);
holder.mListener = item::onClick;
}

View File

@@ -12,6 +12,7 @@ public class LayersUtils
availableLayers.add(Mode.OUTDOORS);
availableLayers.add(Mode.ISOLINES);
availableLayers.add(Mode.SUBWAY);
availableLayers.add(Mode.TRAFFIC);
return availableLayers;
}
}

View File

@@ -43,20 +43,19 @@ public class TrafficButton
void turnOff()
{
stopWaitingAnimation();
mButton.setImageResource(ThemeUtils.isNightTheme() ? R.drawable.ic_traffic_on_night : R.drawable.ic_traffic_on);
mButton.setImageResource(R.drawable.ic_traffic_on);
}
void turnOn()
{
stopWaitingAnimation();
mButton.setImageResource(ThemeUtils.isNightTheme() ? R.drawable.ic_traffic_on_night : R.drawable.ic_traffic_on);
mButton.setImageResource(R.drawable.ic_traffic_on);
}
void markAsOutdated()
{
stopWaitingAnimation();
mButton.setImageResource(ThemeUtils.isNightTheme() ? R.drawable.ic_traffic_outdated_night
: R.drawable.ic_traffic_outdated);
mButton.setImageResource(R.drawable.ic_traffic_outdated);
}
void startWaitingAnimation()

View File

@@ -20,7 +20,6 @@ import com.google.android.material.textview.MaterialTextView;
import app.organicmaps.R;
import app.organicmaps.sdk.search.DisplayedCategories;
import app.organicmaps.sdk.util.Language;
import app.organicmaps.util.ThemeUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Locale;
@@ -91,11 +90,7 @@ class CategoriesAdapter extends RecyclerView.Adapter<CategoriesAdapter.ViewHolde
@DrawableRes
private static int getDrawableResIdByKey(@NonNull Context context, @NonNull String packageName, @NonNull String key)
{
final boolean isNightTheme = ThemeUtils.isNightTheme();
String iconId = "ic_" + key;
if (isNightTheme)
iconId = iconId + "_night";
return context.getResources().getIdentifier(iconId, "drawable", packageName);
return context.getResources().getIdentifier("ic_" + key, "drawable", packageName);
}
@Override

View File

@@ -33,13 +33,6 @@ public class SearchActivity extends BaseMwmFragmentActivity
activity.startActivity(i);
}
@Override
@StyleRes
public int getThemeResourceId(@NonNull String theme)
{
return ThemeUtils.getCardBgThemeResourceId(theme);
}
@Override
protected Class<? extends Fragment> getFragmentClass()
{

View File

@@ -12,7 +12,6 @@ import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat;
import androidx.recyclerview.widget.RecyclerView;
import app.organicmaps.R;
import app.organicmaps.util.ThemeUtils;
import app.organicmaps.util.Utils;
import app.organicmaps.util.WindowInsetUtils.ScrollableContentInsetsListener;
@@ -35,24 +34,17 @@ abstract class BaseXmlSettingsFragment extends PreferenceFragmentCompat
}
@Override
public void onAttach(Context context)
public void onAttach(@NonNull Context context)
{
super.onAttach(context);
Utils.detachFragmentIfCoreNotInitialized(context, this);
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState)
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState)
{
super.onViewCreated(view, savedInstanceState);
int color;
if (ThemeUtils.isDefaultTheme())
color = ContextCompat.getColor(requireContext(), R.color.bg_cards);
else
color = ContextCompat.getColor(requireContext(), R.color.bg_cards_night);
view.setBackgroundColor(color);
view.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.bg_cards));
RecyclerView recyclerView = getListView();
ViewCompat.setOnApplyWindowInsetsListener(recyclerView, new ScrollableContentInsetsListener(recyclerView));
}

View File

@@ -107,6 +107,11 @@ public class DrivingOptionsFragment extends BaseMwmToolbarFragment
dirtyRoadsBtn.setChecked(RoutingOptions.hasOption(RoadType.Dirty));
CompoundButton.OnCheckedChangeListener dirtyBtnListener = new ToggleRoutingOptionListener(RoadType.Dirty);
dirtyRoadsBtn.setOnCheckedChangeListener(dirtyBtnListener);
SwitchCompat stepsBtn = root.findViewById(R.id.avoid_steps_btn);
stepsBtn.setChecked(RoutingOptions.hasOption(RoadType.Steps));
CompoundButton.OnCheckedChangeListener stepsBtnListener = new ToggleRoutingOptionListener(RoadType.Steps);
stepsBtn.setOnCheckedChangeListener(stepsBtnListener);
}
private static class ToggleRoutingOptionListener implements CompoundButton.OnCheckedChangeListener

View File

@@ -3,12 +3,19 @@ package app.organicmaps.settings;
import static app.organicmaps.leftbutton.LeftButtonsHolder.DISABLE_BUTTON_CODE;
import android.annotation.SuppressLint;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Bundle;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.preference.EditTextPreference;
import androidx.preference.ListPreference;
import androidx.preference.MultiSelectListPreference;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.TwoStatePreference;
@@ -28,6 +35,7 @@ import app.organicmaps.sdk.routing.RoutingOptions;
import app.organicmaps.sdk.search.SearchRecents;
import app.organicmaps.sdk.settings.MapLanguageCode;
import app.organicmaps.sdk.settings.UnitLocale;
import app.organicmaps.sdk.traffxml.AndroidTransport;
import app.organicmaps.sdk.util.Config;
import app.organicmaps.sdk.util.NetworkPolicy;
import app.organicmaps.sdk.util.PowerManagment;
@@ -35,11 +43,13 @@ import app.organicmaps.sdk.util.SharedPropertiesUtils;
import app.organicmaps.sdk.util.log.LogsManager;
import app.organicmaps.util.ThemeSwitcher;
import app.organicmaps.util.Utils;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import java.util.Set;
public class SettingsPrefsFragment extends BaseXmlSettingsFragment implements LanguagesFragment.Listener
{
@@ -50,7 +60,7 @@ public class SettingsPrefsFragment extends BaseXmlSettingsFragment implements La
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState)
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState)
{
super.onViewCreated(view, savedInstanceState);
@@ -61,6 +71,10 @@ public class SettingsPrefsFragment extends BaseXmlSettingsFragment implements La
initAutoDownloadPrefsCallbacks();
initLargeFontSizePrefsCallbacks();
initTransliterationPrefsCallbacks();
initTrafficHttpEnabledPrefsCallbacks();
initTrafficHttpUrlPrefsCallbacks();
initTrafficAppsPrefs();
initTrafficLegacyEnabledPrefsCallbacks();
init3dModePrefsCallbacks();
initPerspectivePrefsCallbacks();
initAutoZoomPrefsCallbacks();
@@ -136,6 +150,46 @@ public class SettingsPrefsFragment extends BaseXmlSettingsFragment implements La
pref.setSummary(locale.getDisplayLanguage());
}
private void updateTrafficHttpUrlSummary()
{
final Preference pref = getPreference(getString(R.string.pref_traffic_http_url));
String summary = Config.getTrafficHttpUrl();
if (summary.length() == 0)
pref.setSummary(R.string.traffic_http_url_not_set);
else
pref.setSummary(summary);
}
private void updateTrafficAppsSummary()
{
final MultiSelectListPreference pref = getPreference(getString(R.string.pref_traffic_apps));
/*
* If the preference is disabled, it has not been initialized. This is the case if no TraFF
* apps were found. The code below would crash when trying to access the entries, and there
* is no need to update the summary if the setting cannot be changed.
*/
if (!pref.isEnabled())
return;
String[] apps = Config.getTrafficApps();
if (apps.length == 0)
pref.setSummary(R.string.traffic_apps_none_selected);
else
{
String summary = "";
for (int i = 0; i < apps.length; i++)
{
if (i > 0)
summary = summary + ", ";
int index = pref.findIndexOfValue(apps[i]);
if (i >= 0)
summary = summary + pref.getEntries()[index];
else
summary = summary + apps[i];
}
pref.setSummary(summary);
}
}
private void updateRoutingSettingsPrefsSummary()
{
final Preference pref = getPreference(getString(R.string.prefs_routing));
@@ -163,6 +217,8 @@ public class SettingsPrefsFragment extends BaseXmlSettingsFragment implements La
updateVoiceInstructionsPrefsSummary();
updateRoutingSettingsPrefsSummary();
updateMapLanguageCodeSummary();
updateTrafficHttpUrlSummary();
updateTrafficAppsSummary();
}
@Override
@@ -224,6 +280,91 @@ public class SettingsPrefsFragment extends BaseXmlSettingsFragment implements La
});
}
private void initTrafficHttpEnabledPrefsCallbacks()
{
final Preference pref = getPreference(getString(R.string.pref_traffic_http_enabled));
((TwoStatePreference)pref).setChecked(Config.getTrafficHttpEnabled());
pref.setOnPreferenceChangeListener((preference, newValue) -> {
final boolean oldVal = Config.getTrafficHttpEnabled();
final boolean newVal = (Boolean) newValue;
if (oldVal != newVal)
Config.setTrafficHttpEnabled(newVal);
return true;
});
}
private void initTrafficHttpUrlPrefsCallbacks()
{
final Preference pref = getPreference(getString(R.string.pref_traffic_http_url));
((EditTextPreference)pref).setText(Config.getTrafficHttpUrl());
pref.setOnPreferenceChangeListener((preference, newValue) -> {
final String oldVal = Config.getTrafficHttpUrl();
final String newVal = (String) newValue;
if (!oldVal.equals(newVal))
Config.setTrafficHttpUrl(newVal);
return true;
});
}
private void initTrafficAppsPrefs()
{
final MultiSelectListPreference pref = getPreference(getString(R.string.pref_traffic_apps));
PackageManager pm = getContext().getPackageManager();
List<ResolveInfo> receivers = pm.queryBroadcastReceivers(new Intent(AndroidTransport.ACTION_TRAFF_GET_CAPABILITIES), 0);
if (receivers == null || receivers.isEmpty())
{
pref.setSummary(R.string.traffic_apps_not_available);
pref.setEnabled(false);
return;
}
pref.setEnabled(true);
List<String> entryList = new ArrayList<>(receivers.size());
List<String> valueList = new ArrayList<>(receivers.size());
for (ResolveInfo receiver : receivers)
{
// friendly name
entryList.add(receiver.loadLabel(pm).toString());
// actual value (we just need the package name, broadcasts are sent to any receiver in the package)
valueList.add(receiver.activityInfo.applicationInfo.packageName);
}
pref.setEntries(entryList.toArray(new CharSequence[0]));
pref.setEntryValues(valueList.toArray(new CharSequence[0]));
pref.setOnPreferenceChangeListener((preference, newValue) -> {
// newValue is a Set<String>, each item is a package ID
String[] apps = ((Set<String>)newValue).toArray(new String[0]);
Config.setTrafficApps(apps);
updateTrafficAppsSummary();
return true;
});
}
private void initTrafficLegacyEnabledPrefsCallbacks()
{
final Preference pref = getPreference(getString(R.string.pref_traffic_legacy_enabled));
((TwoStatePreference)pref).setChecked(Config.getTrafficLegacyEnabled());
pref.setOnPreferenceChangeListener((preference, newValue) -> {
final boolean oldVal = Config.getTrafficLegacyEnabled();
final boolean newVal = (Boolean) newValue;
if (oldVal != newVal)
Config.setTrafficLegacyEnabled(newVal);
return true;
});
}
private void initUseMobileDataPrefsCallbacks()
{
final ListPreference mobilePref = getPreference(getString(R.string.pref_use_mobile_data));

View File

@@ -137,7 +137,7 @@ public class VoiceInstructionsSettingsFragment extends BaseXmlSettingsFragment
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState)
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState)
{
super.onViewCreated(view, savedInstanceState);

View File

@@ -86,7 +86,7 @@ public enum ThemeSwitcher
String oldTheme = Config.UiTheme.getCurrent();
MapStyle style;
if (ThemeUtils.isNightTheme())
if (Config.UiTheme.isNight(theme))
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
uiModeManager.setApplicationNightMode(UiModeManager.MODE_NIGHT_YES);

View File

@@ -6,8 +6,6 @@ import android.util.TypedValue;
import androidx.annotation.AttrRes;
import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import androidx.annotation.StyleRes;
import app.organicmaps.R;
import app.organicmaps.sdk.util.Config;
public final class ThemeUtils
@@ -64,28 +62,4 @@ public final class ThemeUtils
{
return Config.UiTheme.isNavAuto(Config.UiTheme.getUiThemeSettings());
}
@StyleRes
public static int getCardBgThemeResourceId(@NonNull String theme)
{
if (Config.UiTheme.isDefault(theme))
return R.style.MwmTheme_CardBg;
if (Config.UiTheme.isNight(theme))
return R.style.MwmTheme_Night_CardBg;
throw new IllegalArgumentException("Attempt to apply unsupported theme: " + theme);
}
@StyleRes
public static int getWindowBgThemeResourceId(@NonNull String theme)
{
if (Config.UiTheme.isDefault(theme))
return R.style.MwmTheme_WindowBg;
if (Config.UiTheme.isNight(theme))
return R.style.MwmTheme_Night_WindowBg;
throw new IllegalArgumentException("Attempt to apply unsupported theme: " + theme);
}
}

View File

@@ -35,9 +35,8 @@ public class RoutingToolbarButton extends AppCompatRadioButton
private void initView()
{
final boolean isNightTheme = ThemeUtils.isNightTheme();
setBackgroundResource(isNightTheme ? R.drawable.routing_toolbar_button_night : R.drawable.routing_toolbar_button);
setButtonTintList(isNightTheme ? R.color.routing_toolbar_icon_tint_night : R.color.routing_toolbar_icon_tint);
setBackgroundResource(R.drawable.routing_toolbar_button);
setButtonTintList(R.color.routing_toolbar_icon_tint);
}
public void progress()

View File

@@ -72,6 +72,7 @@ import app.organicmaps.util.bottomsheet.MenuBottomSheetFragment;
import app.organicmaps.util.bottomsheet.MenuBottomSheetItem;
import app.organicmaps.widget.ArrowView;
import app.organicmaps.widget.placepage.sections.PlacePageBookmarkFragment;
import app.organicmaps.widget.placepage.sections.PlacePageChargeSocketsFragment;
import app.organicmaps.widget.placepage.sections.PlacePageLinksFragment;
import app.organicmaps.widget.placepage.sections.PlacePageOpeningHoursFragment;
import app.organicmaps.widget.placepage.sections.PlacePagePhoneFragment;
@@ -98,6 +99,7 @@ public class PlacePageView extends Fragment
private static final String BOOKMARK_FRAGMENT_TAG = "BOOKMARK_FRAGMENT_TAG";
private static final String TRACK_FRAGMENT_TAG = "TRACK_FRAGMENT_TAG";
private static final String WIKIPEDIA_FRAGMENT_TAG = "WIKIPEDIA_FRAGMENT_TAG";
private static final String CHARGE_SOCKETS_FRAGMENT_TAG = "CHARGE_SOCKETS_FRAGMENT_TAG";
private static final String PHONE_FRAGMENT_TAG = "PHONE_FRAGMENT_TAG";
private static final String OPENING_HOURS_FRAGMENT_TAG = "OPENING_HOURS_FRAGMENT_TAG";
private static final String LINKS_FRAGMENT_TAG = "LINKS_FRAGMENT_TAG";
@@ -146,7 +148,6 @@ public class PlacePageView extends Fragment
private MaterialTextView mTvEntrance;
private MaterialTextView mTvLastChecked;
private View mEditPlace;
private View mAddOrganisation;
private View mAddPlace;
private View mEditTopSpace;
private ShapeableImageView mColorIcon;
@@ -311,7 +312,6 @@ public class PlacePageView extends Fragment
mTvEntrance = mEntrance.findViewById(R.id.tv__place_entrance);
mTvLastChecked = mFrame.findViewById(R.id.place_page_last_checked);
mEditPlace = mFrame.findViewById(R.id.ll__place_editor);
mAddOrganisation = mFrame.findViewById(R.id.ll__add_organisation);
mAddPlace = mFrame.findViewById(R.id.ll__place_add);
mEditTopSpace = mFrame.findViewById(R.id.edit_top_space);
latlon.setOnLongClickListener(this);
@@ -407,6 +407,12 @@ public class PlacePageView extends Fragment
R.id.place_page_opening_hours_fragment, !TextUtils.isEmpty(ohStr));
}
private void updateChargeSocketsView()
{
updateViewFragment(PlacePageChargeSocketsFragment.class, CHARGE_SOCKETS_FRAGMENT_TAG,
R.id.place_page_charge_sockets_fragment, mMapObject.hasChargeSockets());
}
private void updatePhoneView()
{
updateViewFragment(PlacePagePhoneFragment.class, PHONE_FRAGMENT_TAG, R.id.place_page_phone_fragment,
@@ -665,28 +671,25 @@ public class PlacePageView extends Fragment
if (!lastChecked.isEmpty())
{
String periodSinceCheck = DateUtils.getRelativePeriodString(getResources(), lastChecked);
UiUtils.setTextAndShow(mTvLastChecked, requireContext().getString(R.string.existence_confirmed_time_ago, periodSinceCheck));
UiUtils.setTextAndShow(mTvLastChecked,
requireContext().getString(R.string.existence_confirmed_time_ago, periodSinceCheck));
}
else
UiUtils.hide(mTvLastChecked);
if (RoutingController.get().isNavigating() || RoutingController.get().isPlanning())
{
UiUtils.hide(mEditPlace, mAddOrganisation, mAddPlace, mEditTopSpace);
UiUtils.hide(mEditPlace, mAddPlace, mEditTopSpace);
}
else
{
UiUtils.showIf(Editor.nativeShouldShowEditPlace(), mEditPlace);
UiUtils.showIf(Editor.nativeShouldShowAddBusiness(), mAddOrganisation);
UiUtils.showIf(Editor.nativeShouldShowAddPlace(), mAddPlace);
MaterialButton mTvEditPlace = mEditPlace.findViewById(R.id.mb__place_editor);
MaterialButton mTvAddBusiness = mAddOrganisation.findViewById(R.id.mb__add_organisation);
MaterialButton mTvAddPlace = mAddPlace.findViewById(R.id.mb__place_add);
mTvEditPlace.setOnClickListener(this);
mTvAddBusiness.setOnClickListener(this);
mTvAddPlace.setOnClickListener(this);
mTvEditPlace.setEnabled(Editor.nativeShouldEnableEditPlace());
mTvAddBusiness.setEnabled(Editor.nativeShouldEnableAddPlace());
mTvAddPlace.setEnabled(Editor.nativeShouldEnableAddPlace());
final int editPlaceButtonColor =
Editor.nativeShouldEnableEditPlace()
@@ -695,19 +698,18 @@ public class PlacePageView extends Fragment
UiUtils.getStyledResourceId(getContext(), com.google.android.material.R.attr.colorSecondary))
: ContextCompat.getColor(getContext(), R.color.button_accent_text_disabled);
mTvEditPlace.setTextColor(editPlaceButtonColor);
mTvAddBusiness.setTextColor(editPlaceButtonColor);
mTvAddPlace.setTextColor(editPlaceButtonColor);
mTvEditPlace.setStrokeColor(ColorStateList.valueOf(editPlaceButtonColor));
mTvAddBusiness.setStrokeColor(ColorStateList.valueOf(editPlaceButtonColor));
mTvAddPlace.setStrokeColor(ColorStateList.valueOf(editPlaceButtonColor));
UiUtils.showIf(
UiUtils.isVisible(mEditPlace) || UiUtils.isVisible(mAddOrganisation) || UiUtils.isVisible(mAddPlace),
UiUtils.isVisible(mEditPlace) || UiUtils.isVisible(mAddPlace),
mEditTopSpace);
}
updateLinksView();
updateOpeningHoursView();
updateWikipediaView();
updateBookmarkView();
updateChargeSocketsView();
updatePhoneView();
updateTrackView();
}
@@ -837,11 +839,6 @@ public class PlacePageView extends Fragment
UiUtils.hide(mTvOpenState);
}
private void addOrganisation()
{
((MwmActivity) requireActivity()).showPositionChooserForEditor(true, true);
}
private void addPlace()
{
((MwmActivity) requireActivity()).showPositionChooserForEditor(false, true);
@@ -859,8 +856,6 @@ public class PlacePageView extends Fragment
}
else if (id == R.id.mb__place_editor)
((MwmActivity) requireActivity()).showEditor();
else if (id == R.id.mb__add_organisation)
addOrganisation();
else if (id == R.id.mb__place_add)
addPlace();
else if (id == R.id.ll__place_latlon)

View File

@@ -0,0 +1,121 @@
package app.organicmaps.widget.placepage.sections;
import android.annotation.SuppressLint;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.GridLayout;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
import com.google.android.material.imageview.ShapeableImageView;
import com.google.android.material.textview.MaterialTextView;
import app.organicmaps.R;
import app.organicmaps.sdk.Framework;
import app.organicmaps.sdk.bookmarks.data.ChargeSocketDescriptor;
import app.organicmaps.sdk.bookmarks.data.MapObject;
import app.organicmaps.sdk.bookmarks.data.Metadata;
import app.organicmaps.widget.placepage.PlacePageViewModel;
import java.text.DecimalFormat;
public class PlacePageChargeSocketsFragment extends Fragment implements Observer<MapObject>
{
private GridLayout mGrid;
private PlacePageViewModel mViewModel;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState)
{
mViewModel = new ViewModelProvider(requireActivity()).get(PlacePageViewModel.class);
return inflater.inflate(R.layout.place_page_charge_sockets_fragment, container, false);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState)
{
super.onViewCreated(view, savedInstanceState);
mGrid = view.findViewById(R.id.socket_grid);
}
@Override
public void onStart()
{
super.onStart();
mViewModel.getMapObject().observe(requireActivity(), this);
}
@Override
public void onStop()
{
super.onStop();
mViewModel.getMapObject().removeObserver(this);
}
@Override
public void onChanged(@Nullable MapObject mapObject)
{
if (mapObject == null)
{
return;
}
mGrid.removeAllViews();
ChargeSocketDescriptor[] sockets = Framework.nativeGetActiveObjectChargeSockets();
LayoutInflater inflater = LayoutInflater.from(requireContext());
for (ChargeSocketDescriptor socket : sockets)
{
View itemView = inflater.inflate(R.layout.item_charge_socket, mGrid, false);
itemView.setClickable(false);
itemView.setFocusable(false);
MaterialTextView type = itemView.findViewById(R.id.socket_type);
ShapeableImageView icon = itemView.findViewById(R.id.socket_icon);
MaterialTextView power = itemView.findViewById(R.id.socket_power);
MaterialTextView count = itemView.findViewById(R.id.socket_count);
// load SVG icon converted into VectorDrawable in res/drawable
@SuppressLint("DiscouragedApi")
int resIconId = getResources().getIdentifier("ic_charge_socket_" + socket.type(), "drawable",
requireContext().getPackageName());
if (resIconId != 0)
{
icon.setImageResource(resIconId);
}
@SuppressLint("DiscouragedApi")
int resTypeId =
getResources().getIdentifier("charge_socket_" + socket.type(), "string", requireContext().getPackageName());
if (resTypeId != 0)
{
type.setText(resTypeId);
}
if (socket.power() != 0)
{
DecimalFormat df = new DecimalFormat("#.##");
power.setText(getString(R.string.kw_label, df.format(socket.power())));
}
if (socket.count() != 0)
{
count.setText(getString(R.string.count_label, socket.count()));
}
mGrid.addView(itemView);
}
}
}

View File

@@ -1,15 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:color="@color/base_accent_night"
android:state_selected="true"/>
<item android:color="@color/base_accent_night"
android:state_checked="true"/>
<item android:color="@color/base_accent_night"
app:layers_enabled="true"/>
<item android:color="@color/white_secondary"/>
</selector>

View File

@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@color/button_accent_normal_night"
android:state_enabled="true"/>
<item android:color="@color/button_accent_disabled_night"/>
</selector>

View File

@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@color/button_normal_night"
android:state_enabled="true"/>
<item android:color="@color/button_disabled_night"/>
</selector>

View File

@@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:state_activated="true"
android:color="@color/routing_button_activated_tint_night"/>
<item
android:state_pressed="true"
android:color="@color/routing_button_pressed_tint"/>
<item
android:color="@color/routing_button_tint"/>
</selector>

View File

Before

Width:  |  Height:  |  Size: 702 B

After

Width:  |  Height:  |  Size: 702 B

View File

Before

Width:  |  Height:  |  Size: 714 B

After

Width:  |  Height:  |  Size: 714 B

View File

Before

Width:  |  Height:  |  Size: 714 B

After

Width:  |  Height:  |  Size: 714 B

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