Compare commits

...

182 Commits

Author SHA1 Message Date
Jean-Baptiste
d29823e27b [fdroid] Release version 2025.11.01-7
Signed-off-by: Jean-Baptiste <jeanbaptiste.charron@outlook.fr>
2025-11-01 21:36:02 +01:00
Jean-Baptiste
ab4cae961f Release notes for 2nd October release
Signed-off-by: Jean-Baptiste <jeanbaptiste.charron@outlook.fr>
2025-11-01 21:29:24 +01:00
Codeberg Translate
1f0c202f62 [strings] Update from Codeberg Translate
Co-authored-by: Codeberg Translate <translate@codeberg.org>
Co-authored-by: ERYpTION <eryption@noreply.codeberg.org>
Co-authored-by: Edgarsons <edgarsons@noreply.codeberg.org>
Co-authored-by: JanezPavelZebovec <janezpavelzebovec@noreply.codeberg.org>
Co-authored-by: Juno Takano <jutty@noreply.codeberg.org>
Co-authored-by: Kachelkaiser <kachelkaiser@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: ToninoThePro <toninothepro@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: aoxa <aoxa@noreply.codeberg.org>
Co-authored-by: guille11 <guille11@noreply.codeberg.org>
Co-authored-by: hippietivu <hippietivu@noreply.codeberg.org>
Co-authored-by: matheusgomesms <matheusgomesms@noreply.codeberg.org>
Co-authored-by: ovl-005 <ovl-005@noreply.codeberg.org>
Co-authored-by: sunsand <sunsand@noreply.codeberg.org>
Co-authored-by: tagomagic <tagomagic@noreply.codeberg.org>
Co-authored-by: teletext <teletext@noreply.codeberg.org>
Co-authored-by: x7z4w <x7z4w@noreply.codeberg.org>
Co-authored-by: 玄枵 <ipmlosion@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/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-11-01 18:58:21 +00:00
Yannik Bloscheck
d9354e0bd1 [drape] Don't apply bus reference when there is no main title
Signed-off-by: Yannik Bloscheck <git@yannikbloscheck.com>
2025-11-01 19:58:01 +01:00
Jean-Baptiste
f443d3a207 [planet] Update map data to 251028
Signed-off-by: Jean-Baptiste <jeanbaptiste.charron@outlook.fr>
2025-11-01 11:16:46 +01:00
Jean-Baptiste
98fe26f4ab [android] Remove useless tools property in views
Signed-off-by: Jean-Baptiste <jeanbaptiste.charron@outlook.fr>
2025-11-01 10:28:12 +01:00
map-per
f6be1b0ebf Hide charging sockets in editor until #2368 is fixed
Signed-off-by: map-per <map-per@gmx.de>
2025-11-01 09:48:44 +01:00
Yannik Bloscheck
0e3b6264b8 [styles] Fix event venue label color
Signed-off-by: Yannik Bloscheck <git@yannikbloscheck.com>
2025-10-31 20:13:46 +01:00
Jean-Baptiste
f5edbb33f8 [android] Remove old webp used on unused views
Signed-off-by: Jean-Baptiste <jeanbaptiste.charron@outlook.fr>
2025-10-31 13:46:24 +01:00
Yannik Bloscheck
81c59fe2c9 [styles] Deduplicate colored labels
Signed-off-by: Yannik Bloscheck <git@yannikbloscheck.com>
2025-10-30 12:53:53 +01:00
Alexander Borsuk
25d004f7b3 Add OM version to uploaded OSM notes
Signed-off-by: Alexander Borsuk <me@alex.bio>
2025-10-30 11:22:58 +01:00
x7z4w
210b4df8b2 [drape] Fix sqrt constexpr
Signed-off-by: x7z4w <x7z4w@noreply.codeberg.org>
2025-10-29 10:17:15 +00:00
Jean-Baptiste
12b9504daa [android] Unify border radius in navigation
Signed-off-by: Jean-Baptiste <jeanbaptiste.charron@outlook.fr>
2025-10-29 06:33:33 +01:00
x7z4w
ae9dc62b54 [strings] Add synonyms
Signed-off-by: x7z4w <x7z4w@noreply.codeberg.org>
2025-10-28 22:17:28 +01:00
x7z4w
42f059f8f7 [core] Use constexpr when possible
Signed-off-by: x7z4w <x7z4w@noreply.codeberg.org>
2025-10-28 22:11:19 +01:00
Dobri Dabar
936e05283c [desktop] Sort map languages in the dropdown list in preferences
Signed-off-by: Dobri Dabar <dobridabar@noreply.codeberg.org>
2025-10-28 21:38:28 +01:00
Codeberg Translate
6cee8ee389 [strings] Update from Codeberg Translate
Co-authored-by: Codeberg Translate <translate@codeberg.org>
Co-authored-by: Fjuro <fjuro@alius.cz>
Co-authored-by: JanezPavelZebovec <janezpavelzebovec@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: Weblate <noreply-mt-weblate@weblate.org>
Co-authored-by: Weblate Translation Memory <noreply-mt-weblate-translation-memory@weblate.org>
Co-authored-by: javnik <javnik@noreply.codeberg.org>
Translation: CoMaps/Android - Map Feature Types
Translation: CoMaps/Android UI Strings
Translation: CoMaps/iOS - Map Feature Types
Translation: CoMaps/iOS UI Strings
2025-10-28 18:11:24 +00:00
Yannik Bloscheck
d92a26739b [styles] Fix wrong icon for guest houses as certain zoom levels
Signed-off-by: Yannik Bloscheck <git@yannikbloscheck.com>
2025-10-28 14:05:55 +01:00
Yannik Bloscheck
be906c7973 [styles] Fix wrong label color for massage salons
Signed-off-by: Yannik Bloscheck <git@yannikbloscheck.com>
2025-10-28 14:05:55 +01:00
x7z4w
d1ff4f8c60 [strings] Add default language to countries
Signed-off-by: x7z4w <x7z4w@noreply.codeberg.org>
2025-10-28 13:22:59 +01:00
Codeberg Translate
9b29191e63 [strings] Update from Codeberg Translate
Co-authored-by: Alexey Ladygin <nitrodox@noreply.codeberg.org>
Co-authored-by: Anonymous <anonymous@noreply.codeberg.org>
Co-authored-by: Burak Onder <burakonder@noreply.codeberg.org>
Co-authored-by: Codeberg Translate <translate@codeberg.org>
Co-authored-by: ERYpTION <eryption@noreply.codeberg.org>
Co-authored-by: Edgarsons <edgarsons@noreply.codeberg.org>
Co-authored-by: Edison_Lu <edison_lu@noreply.codeberg.org>
Co-authored-by: Fjuro <fjuro@alius.cz>
Co-authored-by: JanezPavelZebovec <janezpavelzebovec@noreply.codeberg.org>
Co-authored-by: Juno Takano <jutty@noreply.codeberg.org>
Co-authored-by: Mickael81 <mickael81@noreply.codeberg.org>
Co-authored-by: N4ta <codeberg@n4ta.anonaddy.me>
Co-authored-by: Outbreak2096 <outbreak2096@noreply.codeberg.org>
Co-authored-by: Pamputt <pamputt@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: Stzyxh <stzyxh@noreply.codeberg.org>
Co-authored-by: ToninoThePro <toninothepro@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: gedankenstuecke <gedankenstuecke@noreply.codeberg.org>
Co-authored-by: hectodium <hectodium@noreply.codeberg.org>
Co-authored-by: ikanakova <ikanakova@noreply.codeberg.org>
Co-authored-by: jeanbaptisteC <jeanbaptistec@noreply.codeberg.org>
Co-authored-by: ldmpub <ldmpub@noreply.codeberg.org>
Co-authored-by: loscati <loscati@noreply.codeberg.org>
Co-authored-by: map-per <map-per@noreply.codeberg.org>
Co-authored-by: matheusgomesms <matheusgomesms@noreply.codeberg.org>
Co-authored-by: metehan <metehan@noreply.codeberg.org>
Co-authored-by: oleg-rswll <oleg-rswll@noreply.codeberg.org>
Co-authored-by: ovl-005 <ovl-005@noreply.codeberg.org>
Co-authored-by: patepelo <patepelo@noreply.codeberg.org>
Co-authored-by: paulb <paulb@noreply.codeberg.org>
Co-authored-by: phama <phama@noreply.codeberg.org>
Co-authored-by: polarwood <polarwood@noreply.codeberg.org>
Co-authored-by: rimas <rimas@noreply.codeberg.org>
Co-authored-by: summoner <summoner@noreply.codeberg.org>
Co-authored-by: teletext <teletext@noreply.codeberg.org>
Co-authored-by: tsobuskerudbyen <tsobuskerudbyen@noreply.codeberg.org>
Co-authored-by: x7z4w <x7z4w@noreply.codeberg.org>
Co-authored-by: yurtpage <yurtpage@noreply.codeberg.org>
Translate-URL: https://translate.codeberg.org/projects/comaps/android-typestrings/
Translate-URL: https://translate.codeberg.org/projects/comaps/ios-typestrings/
Translate-URL: https://translate.codeberg.org/projects/comaps/ios/
Translate-URL: https://translate.codeberg.org/projects/comaps/search-synonyms-aliases/
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-28 12:10:42 +00:00
Jean-Baptiste
73accecfd4 [android] Remove call to fullscreen theme
Signed-off-by: Jean-Baptiste <jeanbaptiste.charron@outlook.fr>
2025-10-28 13:09:51 +01:00
Yannik Bloscheck
495d3e91fd [styles] Fix white road shields in dark mode
Signed-off-by: Yannik Bloscheck <git@yannikbloscheck.com>
2025-10-28 11:43:46 +01:00
Yannik Bloscheck
7c92cdafa6 [styles] Fix broken key cutter icon in dark mode
Signed-off-by: Yannik Bloscheck <git@yannikbloscheck.com>
2025-10-28 10:05:15 +01:00
map-per
310287e918 [editor] remove error messages from CS comment and enforce OSM 255 char length limit (#919)
Signed-off-by: map-per <map-per@gmx.de>
2025-10-28 08:53:29 +01:00
Jean-Baptiste
bab74782f8 [android] Remove title list in description list view
Signed-off-by: Jean-Baptiste <jeanbaptiste.charron@outlook.fr>
2025-10-28 06:44:50 +01:00
Yannik Bloscheck
647b66cbd0 [styles] Adding lounger
Signed-off-by: Yannik Bloscheck <git@yannikbloscheck.com>
2025-10-27 20:49:51 +01:00
Yannik Bloscheck
7bd8f8064a [styles] Fix showing ruins icon when castles are also ruins
Signed-off-by: Yannik Bloscheck <git@yannikbloscheck.com>
2025-10-27 20:44:02 +01:00
Jean-Baptiste
2b5930dff8 [android] Support drawable in PlaceHolderView
Signed-off-by: Jean-Baptiste <jeanbaptiste.charron@outlook.fr>
2025-10-27 20:22:40 +01:00
Yannik Bloscheck
d4ac51cc02 [styles] Added guest house icon
Signed-off-by: Yannik Bloscheck <git@yannikbloscheck.com>
2025-10-27 10:32:39 +01:00
patepelo
09ab8322be edit link to support
Signed-off-by: patepelo <developing.anton@gmail.com>

added RU changes
2025-10-27 14:15:41 +08:00
map-per
5886d15e88 [styles] Add disused business POIs (e.g. disused:shop)
Signed-off-by: map-per <map-per@gmx.de>
2025-10-26 16:22:22 +01:00
map-per
da7c3d6710 [strings] Update category names for amenity=studio and shop=coffee
Signed-off-by: map-per <map-per@gmx.de>
2025-10-26 15:38:47 +01:00
x7z4w
b806ab7443 [search] Separate cafe/restaurant
Signed-off-by: x7z4w <x7z4w@noreply.codeberg.org>
2025-10-26 14:52:36 +01:00
Yannik Bloscheck
da68f01ff9 [styles] Show building names only from zoom level 17 on
Signed-off-by: Yannik Bloscheck <git@yannikbloscheck.com>
2025-10-26 14:31:24 +01:00
Yannik Bloscheck
44dc71066c [styles] Add event venue icon
Signed-off-by: Yannik Bloscheck <git@yannikbloscheck.com>
2025-10-26 13:14:53 +01:00
Yannik Bloscheck
c0396e96c6 [styles] Work around missing support for =no
Signed-off-by: Yannik Bloscheck <git@yannikbloscheck.com>
2025-10-26 13:05:38 +01:00
Jean-Baptiste
1b66a9ac73 [android] Replace settings icon with edit icon on edit list menu
Signed-off-by: Jean-Baptiste <jeanbaptiste.charron@outlook.fr>
2025-10-26 10:24:24 +01:00
Jean-Baptiste
22a28d75cc [android] Remove focus to app version when we open about screen
Signed-off-by: Jean-Baptiste <jeanbaptiste.charron@outlook.fr>
2025-10-25 23:11:13 +02:00
Yannik Bloscheck
a730fccf76 Revert "[types] Add cuisines"
This reverts commit 1d80478eac.
2025-10-25 20:19:57 +02:00
Jean-Baptiste
ae14c05559 Remove speed cameras for some countries
Signed-off-by: Jean-Baptiste <jeanbaptiste.charron@outlook.fr>
2025-10-25 18:22:00 +02:00
Jean-Baptiste
9d908b5e3d Update french categories
Signed-off-by: Jean-Baptiste <jeanbaptiste.charron@outlook.fr>
2025-10-25 18:12:41 +02:00
Yannik Bloscheck
3e67c82517 [styles] Recategorize icons
Signed-off-by: Yannik Bloscheck <git@yannikbloscheck.com>
2025-10-25 16:20:39 +02:00
Yannik Bloscheck
5962d60e42 [styles] Recategorize icons
Signed-off-by: Yannik Bloscheck <git@yannikbloscheck.com>
2025-10-25 16:04:11 +02:00
Yannik Bloscheck
e0ee0d9f57 [styles] Add bandstands
Signed-off-by: Yannik Bloscheck <git@yannikbloscheck.com>
2025-10-25 16:02:15 +02:00
Yannik Bloscheck
1d80478eac [types] Add cuisines
Signed-off-by: Yannik Bloscheck <git@yannikbloscheck.com>
2025-10-25 15:53:05 +02:00
Yannik Bloscheck
2daf40479a [styles] Show police and fire stations earlier
Signed-off-by: Yannik Bloscheck <git@yannikbloscheck.com>
2025-10-25 15:41:42 +02:00
Yannik Bloscheck
5e7a30df8f [styles] Show drinking water from zoom level 17
Signed-off-by: Yannik Bloscheck <git@yannikbloscheck.com>
2025-10-25 15:38:54 +02:00
Yannik Bloscheck
35d4e8af8b [styles] Add bandstands
Signed-off-by: Yannik Bloscheck <git@yannikbloscheck.com>
2025-10-25 15:31:50 +02:00
Yannik Bloscheck
20bfa0a1ad [styles] Add backless bench
Signed-off-by: Yannik Bloscheck <git@yannikbloscheck.com>
2025-10-25 15:23:53 +02:00
Yannik Bloscheck
363f892752 [styles] Add specific icon for massage salons
Signed-off-by: Yannik Bloscheck <git@yannikbloscheck.com>
2025-10-25 15:10:36 +02:00
Yannik Bloscheck
d01ed97e34 [styles] Recategorize icons
Signed-off-by: Yannik Bloscheck <git@yannikbloscheck.com>
2025-10-25 13:11:06 +02:00
map-per
6e12ede1d9 rewrite level validation to support more than one value and ranges
Signed-off-by: map-per <map-per@gmx.de>
2025-10-25 10:38:30 +02:00
zyphlar
90dbfd6311 Use protobuf, new branch
Signed-off-by: zyphlar <zyphlar@noreply.codeberg.org>
2025-10-25 02:56:46 +02:00
zyphlar
45cf6130f0 Add map generator ci/cd script
Supports semi-generic runners (that have /media/4tbexternal mapped)

Signed-off-by: zyphlar <zyphlar@noreply.codeberg.org>
2025-10-24 15:42:15 -07:00
Jean-Baptiste
8a6ce5a561 [android] Add margin around title on the first screen
Signed-off-by: Jean-Baptiste <jeanbaptiste.charron@outlook.fr>
2025-10-24 22:23:15 +02:00
Jean-Baptiste
5bf4a8296c [android] Fix icon tint of edit bookmark button
Signed-off-by: Jean-Baptiste <jeanbaptiste.charron@outlook.fr>
2025-10-24 17:47:31 +02:00
x7z4w
a6314cc53a [core] Fix warning
Signed-off-by: x7z4w <x7z4w@noreply.codeberg.org>
2025-10-24 11:39:24 +02:00
map-per
2820fbac46 Add attribution for OSM Carto (#2237)
Signed-off-by: map-per <map-per@gmx.de>
2025-10-23 17:50:51 +02:00
eisa01
7afb1df917 [core] Only include branch in search highlight range on iOS
Can be removed when other platforms incorporate branch in UI

Signed-off-by: eisa01 <eisa01@gmail.com>
2025-10-23 17:24:37 +02:00
x7z4w
042d497a5e [search] Add multi-token synonyms
Signed-off-by: x7z4w <x7z4w@noreply.codeberg.org>
Co-authored-by: patepelo <developing.anton@gmail.com>
2025-10-23 13:13:56 +02:00
Yannik Bloscheck
6dcf4b039e [styles] Show public transport stop reference on map
Signed-off-by: Yannik Bloscheck <git@yannikbloscheck.com>
2025-10-23 12:57:19 +02:00
Yannik Bloscheck
44f0db083f [styles] Making railway tunnels more inconspicuous
Signed-off-by: Yannik Bloscheck <git@yannikbloscheck.com>
2025-10-23 12:56:40 +02:00
Yannik Bloscheck
1c4b2ea991 [styles] Add specific icon for light rail
Signed-off-by: Yannik Bloscheck <git@yannikbloscheck.com>
2025-10-23 12:56:18 +02:00
Yannik Bloscheck
658c3c0af8 [styles] Added custom road shields for Italian autostrada
Signed-off-by: Yannik Bloscheck <git@yannikbloscheck.com>
2025-10-23 12:56:06 +02:00
Yannik Bloscheck
f6e7580aff [styles] Add pill shapes for road shields (and reorganized the existing logic)
Signed-off-by: Yannik Bloscheck <git@yannikbloscheck.com>
2025-10-23 12:56:06 +02:00
Jean-Baptiste
8e5c1a4d17 Reorganize and clean maxspeed:type values
Signed-off-by: Jean-Baptiste <jeanbaptiste.charron@outlook.fr>
2025-10-23 10:01:18 +02:00
x7z4w
239332edb4 [generator] Support more maxspeed from the wiki
Signed-off-by: x7z4w <x7z4w@noreply.codeberg.org>
2025-10-23 10:01:18 +02:00
Jean-Baptiste
564c6203eb [generator] Support more maxspeed type in the world
Signed-off-by: Jean-Baptiste <jeanbaptiste.charron@outlook.fr>
2025-10-23 10:01:18 +02:00
Isira Seneviratne
c727fd7f20 [android] Simplify TTS code using AudioManagerCompat
Signed-off-by: Isira Seneviratne <31027858+Isira-Seneviratne@users.noreply.github.com>
2025-10-23 09:54:16 +02:00
javnik
5cd9f1274b [routing] Support PL:expressway instead of PL:trunk
Signed-off-by: javnik <javnik@noreply.codeberg.org>
2025-10-22 16:38:35 +02:00
Jean-Baptiste
c8a965c855 [android] Update Android dependencies
Signed-off-by: Jean-Baptiste <jeanbaptiste.charron@outlook.fr>
2025-10-22 12:07:23 +02:00
map-per
0d4099b555 Use map selection instead of mapObject center as OSM note position
Signed-off-by: map-per <map-per@gmx.de>
2025-10-21 19:27:15 +02:00
x7z4w
08bdf41bae [build] Do not build tests on release
Signed-off-by: x7z4w <x7z4w@noreply.codeberg.org>
2025-10-21 19:14:47 +02:00
Jean-Baptiste
6d28928f58 [android] Remove end icon in dialog socket view
Signed-off-by: Jean-Baptiste <jeanbaptiste.charron@outlook.fr>
2025-10-21 18:39:40 +02:00
Jean-Baptiste
d748e4f4f2 [android] Bump targetSDK to 36
Signed-off-by: Jean-Baptiste <jeanbaptiste.charron@outlook.fr>
2025-10-21 11:32:52 +02:00
Jean-Baptiste
a373a3151d [android] Use SVG assets for avoid icons
Signed-off-by: Jean-Baptiste <jeanbaptiste.charron@outlook.fr>
2025-10-21 07:24:32 +02:00
lluka
7b1ed86a5f [android] Make from- and to-time pickers save input mode while picking time
Signed-off-by: lluka <lluka@noreply.codeberg.org>
2025-10-20 22:04:23 +02:00
lluka
0eaa770b21 [android] Use MaterialTimePicker when editing opening hours
Signed-off-by: lluka <lluka@noreply.codeberg.org>
2025-10-20 22:04:23 +02:00
Jean-Baptiste
be3205a82d [android] Use material divider instead view with background
Signed-off-by: Jean-Baptiste <jeanbaptiste.charron@outlook.fr>
2025-10-20 18:58:46 +02:00
Bastian Greshake Tzovaras
dc20e576f7 [taginfo] update for CoMaps
Signed-off-by: Bastian Greshake Tzovaras <bgreshake@googlemail.com>
2025-10-20 16:11:42 +02:00
David Martinez
7915b85f58 [taginfo] Fix timestamp format 2025-10-20 16:11:42 +02:00
David Martinez
6fe73a2e5c [taginfo] Add generate_taginfo.py
Signed-off-by: David Martinez <47610359+dvdmrtnz@users.noreply.github.com>
2025-10-20 16:11:42 +02:00
Jean-Baptiste
9dc3ccae4f [doc] Remove custom color to see red color when CI fails
Signed-off-by: Jean-Baptiste <jeanbaptiste.charron@outlook.fr>
2025-10-20 14:57:02 +02:00
map-per
448f21bb44 update docs
Signed-off-by: map-per <map-per@gmx.de>
2025-10-20 13:58:38 +02:00
Jean-Baptiste
b5d65a0f78 [android] Improve edit bookmark button on the place page
Signed-off-by: Jean-Baptiste <jeanbaptiste.charron@outlook.fr>
2025-10-19 20:49:48 +02:00
Jean-Baptiste
6d774667c5 [android] Fix warnings about ColorStateList
Signed-off-by: Jean-Baptiste <jeanbaptiste.charron@outlook.fr>
2025-10-19 14:22:23 +02:00
Yannik Bloscheck
053f35b519 [ios] Display navigation voice and change instructions in settings
Signed-off-by: Yannik Bloscheck <git@yannikbloscheck.com>
2025-10-19 13:17:27 +02:00
Jean-Baptiste
96d51dfcf9 [editor] Support more postcode format
Signed-off-by: Jean-Baptiste <jeanbaptiste.charron@outlook.fr>
2025-10-19 10:40:25 +02:00
Yannik Bloscheck
cf1432cbf7 Remove speed cameras in Germany
Signed-off-by: Yannik Bloscheck <git@yannikbloscheck.com>
2025-10-19 09:32:20 +02:00
x7z4w
9ae005ac56 [map] Don't save routing viewport
Signed-off-by: x7z4w <x7z4w@noreply.codeberg.org>
2025-10-19 09:30:02 +02:00
x7z4w
bdf9a335ae [tools] Add txt to JSON categories conversion
Signed-off-by: x7z4w <x7z4w@noreply.codeberg.org>
2025-10-18 20:14:20 +00:00
Codeberg Translate
422b14d31a [strings] Update from Codeberg Translate
Co-authored-by: Codeberg Translate <translate@codeberg.org>
Co-authored-by: Fjuro <fjuro@alius.cz>
Co-authored-by: Prefill add-on <noreply-addon-prefill@weblate.org>
Co-authored-by: Weblate <noreply-mt-weblate@weblate.org>
Co-authored-by: dobridabar <dobridabar@noreply.codeberg.org>
Co-authored-by: matheusgomesms <matheusgomesms@noreply.codeberg.org>
Co-authored-by: patepelo <patepelo@noreply.codeberg.org>
Co-authored-by: summoner <summoner@noreply.codeberg.org>
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-18 18:41:37 +00:00
x7z4w
ee30622940 [editor] Support Andorra postcodes
Signed-off-by: x7z4w <x7z4w@noreply.codeberg.org>
2025-10-18 20:05:24 +02:00
Jean-Baptiste
1e931c81d2 [android] Re-enable socket cardview and improve dialog
Signed-off-by: Jean-Baptiste <jeanbaptiste.charron@outlook.fr>
2025-10-18 19:48:34 +02:00
x7z4w
517cfb656a [strings] steps -> stairs
Signed-off-by: x7z4w <x7z4w@noreply.codeberg.org>
2025-10-18 19:45:18 +02:00
x7z4w
7b1a4ed7ac nit
Signed-off-by: x7z4w <x7z4w@noreply.codeberg.org>
2025-10-18 18:02:38 +02:00
x7z4w
8926a9fe16 Update data/faq.html
Signed-off-by: x7z4w <x7z4w@noreply.codeberg.org>
2025-10-18 18:02:38 +02:00
Jean-Baptiste
bdadd2689c Update sentence
Signed-off-by: Jean-Baptiste <jeanbaptiste.charron@outlook.fr>
2025-10-18 18:02:38 +02:00
x7z4w
c399d5e415 Update data/faq.html
Signed-off-by: x7z4w <x7z4w@noreply.codeberg.org>
2025-10-18 18:02:38 +02:00
x7z4w
7feeb6eca6 Update data/faq.html
Signed-off-by: x7z4w <x7z4w@noreply.codeberg.org>
2025-10-18 18:02:38 +02:00
Jean-Baptiste
547ff5795e Updata FAQ about Android version
Signed-off-by: Jean-Baptiste <jeanbaptiste.charron@outlook.fr>
2025-10-18 18:02:38 +02:00
Jean-Baptiste
de5b61fde0 Update links in copyright file
Signed-off-by: Jean-Baptiste <jeanbaptiste.charron@outlook.fr>
2025-10-18 17:57:20 +02:00
x7z4w
59bbea44a5 [styles] Sync vehicle label colors
Signed-off-by: x7z4w <x7z4w@noreply.codeberg.org>
2025-10-18 17:55:42 +02:00
Jean-Baptiste
9ca9704bd9 [android] Improve tools texts
Signed-off-by: Jean-Baptiste jeanbaptiste.charron@outlook.fr
Co-authored-by: Jean-Baptiste <jeanbaptiste.charron@outlook.fr>
Co-committed-by: Jean-Baptiste <jeanbaptiste.charron@outlook.fr>
2025-10-18 14:24:01 +02:00
Jean-Baptiste
d283fd41ae [android] Improve colors in navigation UI
Signed-off-by: Jean-Baptiste <jeanbaptiste.charron@outlook.fr>
2025-10-18 08:55:35 +02:00
Codeberg Translate
f81eff2e33 [strings] Update from Codeberg Translate
Co-authored-by: 2lab <2lab@noreply.codeberg.org>
Co-authored-by: Codeberg Translate <translate@codeberg.org>
Co-authored-by: Edgarsons <edgarsons@noreply.codeberg.org>
Co-authored-by: JanezPavelZebovec <janezpavelzebovec@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: Ricky-Tigg <ricky-tigg@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: gedankenstuecke <gedankenstuecke@noreply.codeberg.org>
Co-authored-by: ghose <ghose@noreply.codeberg.org>
Co-authored-by: javnik <javnik@noreply.codeberg.org>
Co-authored-by: map-per <map-per@noreply.codeberg.org>
Co-authored-by: ovl-005 <ovl-005@noreply.codeberg.org>
Co-authored-by: potatofury <potatofury@noreply.codeberg.org>
Co-authored-by: summoner <summoner@noreply.codeberg.org>
Co-authored-by: teletext <teletext@noreply.codeberg.org>
Co-authored-by: x7z4w <x7z4w@noreply.codeberg.org>
Translate-URL: https://translate.codeberg.org/projects/comaps/fdroid-app-description/
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/iOS - Map Feature Types
Translation: CoMaps/iOS Plurals
Translation: CoMaps/iOS UI Strings
2025-10-17 19:09:32 +00:00
Jean-Baptiste
1d7cf5ae39 [android] Improve phone item view
Signed-off-by: Jean-Baptiste <jeanbaptiste.charron@outlook.fr>
2025-10-17 17:54:28 +02:00
Yannik Bloscheck
0efe782325 [ios] Fix search bar background for POI categories
Signed-off-by: Yannik Bloscheck <git@yannikbloscheck.com>
2025-10-17 15:54:07 +02:00
x7z4w
a1e45b5837 [build] Fix World links
Signed-off-by: x7z4w <x7z4w@noreply.codeberg.org>
2025-10-17 12:11:30 +02:00
x7z4w
7a03dd7cf6 [editor] Fix warning
Signed-off-by: x7z4w <x7z4w@noreply.codeberg.org>
2025-10-17 12:11:00 +02: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
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
x7z4w
340b5ae3f9 [routing] Update mwm_hierarchy_handler
Signed-off-by: x7z4w <x7z4w@noreply.codeberg.org>
2025-10-12 10:59:49 +02: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
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
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
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
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
1244 changed files with 21085 additions and 11686 deletions

View File

@@ -0,0 +1,382 @@
name: map-generator
on:
workflow_dispatch: # Manual trigger
inputs:
jobs:
description: 'Which job(s) to run right now?'
required: true
default: 'all'
type: choice
options:
- all
- copy-coasts
- planet
- wiki
- isolines
- subways
- tiger
- maps
env:
WIKIMEDIA_USERNAME: ${{ secrets.WIKIMEDIA_USERNAME }}
WIKIMEDIA_PASSWORD: ${{ secrets.WIKIMEDIA_PASSWORD }}
S3_KEY_ID: ${{ secrets.S3_KEY_ID }}
S3_SECRET_KEY: ${{ secrets.S3_SECRET_KEY }}
S3_ENDPOINT: ${{ secrets.S3_ENDPOINT }}
S3_BUCKET: ${{ secrets.S3_BUCKET }}
SFTP_USER: ${{ secrets.SFTP_USER }}
SFTP_PASSWORD: ${{ secrets.SFTP_PASSWORD }}
SFTP_HOST: ${{ secrets.SFTP_HOST }}
SFTP_PATH: ${{ secrets.SFTP_PATH }}
DEBIAN_FRONTEND: noninteractive
TZ: Etc/UTC
jobs:
copy-coasts:
if: inputs.jobs == 'copy-coasts' || inputs.jobs == 'all'
name: Copy Previously Generated Coasts
runs-on: mapfilemaker
container:
image: ubuntu:latest
volumes:
- /media/4tbexternal:/media/4tbexternal
concurrency:
group: ${{ github.workflow }}-map-generator-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
steps:
- name: Copy Coasts
shell: bash
run: |
if [ -f /media/4tbexternal/osm-maps/*/intermediate_data/WorldCoasts.geom ]; then
cp /media/4tbexternal/osm-maps/*/intermediate_data/WorldCoasts.geom /media/4tbexternal/osm-planet/latest_coasts.geom
cp /media/4tbexternal/osm-maps/*/intermediate_data/WorldCoasts.rawgeom /media/4tbexternal/osm-planet/latest_coasts.rawgeom
fi
update-planet:
if: inputs.jobs == 'planet' || inputs.jobs == 'all'
name: Update Planet
runs-on: mapfilemaker
container:
image: ubuntu:latest
volumes:
- /media/4tbexternal:/media/4tbexternal
concurrency:
group: ${{ github.workflow }}-map-generator-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
steps:
- name: Install dependencies
shell: bash
run: |
apt-get update -y
apt-get install -y pyosmium osmium-tool python3-venv python3-pip wget2
rm -f /usr/lib/python*/EXTERNALLY-MANAGED
pip3 install "protobuf<4"
- name: Download Planet File if Absent
shell: bash
run: |
if [ ! -d /media/4tbexternal/osm-planet/planet/ ]; then
mkdir -p /media/4tbexternal/osm-planet/planet/
fi
if [ ! -f /media/4tbexternal/osm-planet/planet/planet-latest.osm.pbf ]; then
cd /media/4tbexternal/osm-planet/planet/
wget2 --verbose --progress=bar --continue --debug https://ftpmirror.your.org/pub/openstreetmap/pbf/planet-latest.osm.pbf
fi
- name: Update Planet
shell: bash
run: |
cd /media/4tbexternal/osm-planet/planet/
pyosmium-up-to-date planet-latest.osm.pbf -o planet-latest-new.osm.pbf -vv --size 16384
mv planet-latest-new.osm.pbf planet-latest.osm.pbf
- name: Converting planet-latest.osm.pbf to planet.o5m
run: /root/OM/osmctools/osmconvert planet-latest.osm.pbf -o=planet.o5m
wiki-update:
if: inputs.jobs == 'wiki' || inputs.jobs == 'all'
name: Update Wikipedia
runs-on: mapfilemaker
container:
image: ubuntu:latest
volumes:
- /media/4tbexternal:/media/4tbexternal
concurrency:
group: ${{ github.workflow }}-map-generator-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
steps:
- name: Install dependencies
shell: bash
run: |
apt-get update -y
apt-get install -y jq curl wget2 rustc cargo git ca-certificates
- name: Clone wikiparser if necessary
shell: bash
run: |
if [ ! -d /media/4tbexternal/wikiparser ]; then
cd /media/4tbexternal
git clone https://codeberg.org/comaps/wikiparser.git
fi
- name: Check for planet file
shell: bash
run: |
if [ ! -f /media/4tbexternal/osm-planet/planet/planet-latest.osm.pbf ]; then
echo "ERROR: No file at /media/4tbexternal/osm-planet/planet/planet-latest.osm.pbf"
ls -al /media/4tbexternal/
ls -al /media/4tbexternal/osm-planet/
ls -al /media/4tbexternal/osm-planet/planet/
exit 1
fi
- name: Update Wikipedia from Enterprise API
shell: bash
run: |
mkdir -p /media/4tbexternal/osm-planet/wikipedia/dumps
mkdir -p /media/4tbexternal/osm-planet/wikipedia/build
cd /media/4tbexternal/wikiparser
ls -al
echo "Downloading ..."
./download.sh /media/4tbexternal/osm-planet/wikipedia/dumps
echo "Running ..."
./run.sh /media/4tbexternal/osm-planet/wikipedia/build \
/media/4tbexternal/osm-planet/planet/planet-latest.osm.pbf \
/media/4tbexternal/osm-planet/wikipedia/dumps/latest/*.tar.gz
echo "DONE"
update-isolines:
if: inputs.jobs == 'isolines' || inputs.jobs == 'all'
name: Update Isolines
runs-on: mapfilemaker
container:
image: ubuntu:latest
volumes:
- /media/4tbexternal:/media/4tbexternal
concurrency:
group: ${{ github.workflow }}-map-generator-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
steps:
- name: Install dependencies
shell: bash
run: |
apt-get update -qq \
&& apt-get install -y --no-install-recommends \
curl \
osmctools \
rclone \
git \
ca-certificates \
openssh-client \
sshpass \
vim \
wget \
build-essential \
clang \
cmake \
python3 \
python3-pip \
python3.12-venv \
qt6-base-dev \
qt6-positioning-dev \
libc++-dev \
libfreetype-dev \
libglvnd-dev \
libgl1-mesa-dev \
libharfbuzz-dev \
libicu-dev \
libqt6svg6-dev \
libqt6positioning6-plugins \
libqt6positioning6 \
libsqlite3-dev \
libxrandr-dev \
libxinerama-dev \
libxcursor-dev \
libxi-dev \
zlib1g-dev
rm -f /usr/lib/python*/EXTERNALLY-MANAGED
pip3 install "protobuf<4"
- name: Clone main repo if necessary
shell: bash
run: |
if [ ! -d /media/4tbexternal/comaps-init ]; then
apt-get update -qq && apt-get install -y --no-install-recommends git
cd /media/4tbexternal
git clone --recurse-submodules --shallow-submodules -b rebase-generator-pastk-wb251014 --single-branch https://codeberg.org/comaps/comaps.git comaps-init
fi
- name: Update Isolines
shell: bash
run: |
cd /media/4tbexternal/comaps-init/
./tools/unix/build_omim.sh -R topography_generator_tool
rm -rf ../osm-planet/isolines/
mkdir ../osm-planet/isolines/
../omim-build-relwithdebinfo/topography_generator_tool \
--profiles_path=./data/conf/isolines/isolines-profiles.json \
--countries_to_generate_path=./data/conf/isolines/countries-to-generate.json \
--tiles_isolines_out_dir=../osm-planet/isolines/tmp-tiles/ \
--countries_isolines_out_dir=../osm-planet/isolines/ \
--data_dir=./data/ \
--srtm_path=../osm-planet/SRTM-patched-europe/ \
--threads=22
update-subways:
if: inputs.jobs == 'subways' || inputs.jobs == 'all'
name: Update Subways
runs-on: mapfilemaker
container:
image: ubuntu:latest
volumes:
- /media/4tbexternal:/media/4tbexternal
concurrency:
group: ${{ github.workflow }}-map-generator-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
steps:
- name: Install dependencies
shell: bash
run: |
apt-get update -qq && apt-get install -y --no-install-recommends curl osmctools osmium-tool python3-venv ca-certificates git python3-pip
rm -f /usr/lib/python*/EXTERNALLY-MANAGED
pip3 install "protobuf<4"
- name: Clone subways if necessary
shell: bash
run: |
if [ ! -d /media/4tbexternal/subways ]; then
cd /media/4tbexternal
git clone https://codeberg.org/comaps/subways.git
fi
- name: Clone main repo if necessary
shell: bash
run: |
if [ ! -d /media/4tbexternal/comaps-init ]; then
cd /media/4tbexternal
git clone --recurse-submodules --shallow-submodules -b rebase-generator-pastk-wb251014 --single-branch https://codeberg.org/comaps/comaps.git comaps-init
fi
- name: Update Subways
shell: bash
run: |
cd /media/4tbexternal/comaps-init/
cp tools/unix/maps/settings.sh.prod tools/unix/maps/settings.sh
./tools/unix/maps/generate_subways.sh
update-tiger:
if: inputs.jobs == 'tiger' || inputs.jobs == 'all'
name: Update TIGER
runs-on: mapfilemaker
container:
image: ubuntu:latest
volumes:
- /media/4tbexternal:/media/4tbexternal
concurrency:
group: ${{ github.workflow }}-map-generator-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
steps:
- name: Install dependencies
shell: bash
run: |
apt-get update -qq && apt-get install -y --no-install-recommends \
build-essential \
clang \
cmake \
ninja-build \
ca-certificates \
git \
wget2
- name: Clone main repo if necessary
shell: bash
run: |
if [ ! -d /media/4tbexternal/comaps-init ]; then
cd /media/4tbexternal
git clone --recurse-submodules --shallow-submodules -b rebase-generator-pastk-wb251014 --single-branch https://codeberg.org/comaps/comaps.git comaps-init
fi
- name: Build address_parser
shell: bash
run: |
cd /media/4tbexternal/comaps-init
rm -rf ../omim-build-relwithdebinfo/CMakeCache.txt
rm -rf ../omim-build-relwithdebinfo/CMakeFiles
./tools/unix/build_omim.sh -R address_parser_tool
- name: Update TIGER from Nominatim
shell: bash
run: |
cd /media/4tbexternal/osm-planet/
wget2 https://nominatim.org/data/tiger-nominatim-preprocessed-latest.csv.tar.gz
tar -xOzf tiger-nominatim-preprocessed-latest.csv.tar.gz | /media/4tbexternal/omim-build-relwithdebinfo/address_parser_tool --output_path=./tiger
generate-maps:
if: inputs.jobs == 'maps' || inputs.jobs == 'all'
name: Generate Maps
runs-on: mapfilemaker
container:
image: ubuntu:latest
volumes:
- /media/4tbexternal:/media/4tbexternal
options: --ulimit nofile=262144:262144
concurrency:
group: ${{ github.workflow }}-map-generator-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
steps:
- name: Install dependencies
shell: bash
run: |
apt-get update -qq \
&& apt-get install -y --no-install-recommends \
curl \
osmctools \
rclone \
git \
ca-certificates \
openssh-client \
sshpass \
vim \
wget \
build-essential \
clang \
cmake \
ninja-build \
python3 \
python3-pip \
python3.12-venv \
qt6-base-dev \
qt6-positioning-dev \
libc++-dev \
libfreetype-dev \
libglvnd-dev \
libgl1-mesa-dev \
libharfbuzz-dev \
libicu-dev \
libqt6svg6-dev \
libqt6positioning6-plugins \
libqt6positioning6 \
libsqlite3-dev \
libxrandr-dev \
libxinerama-dev \
libxcursor-dev \
libxi-dev \
zlib1g-dev
- name: Clone repo if necessary
shell: bash
run: |
if [ ! -d /media/4tbexternal/comaps-init ]; then
cd /media/4tbexternal
git clone --recurse-submodules --shallow-submodules -b rebase-generator-pastk-wb251014 --single-branch https://codeberg.org/comaps/comaps.git comaps-init
fi
- name: Make output folders if necessary
shell: bash
run: |
if [ ! -d /media/4tbexternal/osm-maps ]; then
mkdir -p /media/4tbexternal/osm-maps
fi
- name: Get SRTM if necessary
shell: bash
run: |
if [ ! -d /media/4tbexternal/osm-planet/SRTM-patched-europe/ ]; then
echo "ERROR: NO SRTM"
exit 1
fi
- name: Symlink paths for repo scripts
shell: bash
run: |
mkdir -p /root/OM
ln -s /media/4tbexternal/comaps-init /root/OM/organicmaps
ln -s /media/4tbexternal/osm-planet /home/planet
ln -s /media/4tbexternal/osm-maps /root/OM/maps_build
- name: Run docker_maps_generator.sh
shell: bash
run: |
cd /root/OM/organicmaps
./tools/unix/docker_maps_generator.sh

View File

@@ -23,10 +23,10 @@
<img src="https://img.shields.io/github/license/comaps/comaps?style=for-the-badge&logo=opensourceinitiative&logoColor=white&color=588157" alt="License"/>
</a>
<a href="https://github.com/comaps/comaps/actions/workflows/android-check.yaml">
<img src="https://img.shields.io/github/actions/workflow/status/comaps/comaps/.github/workflows/android-check.yaml?label=Android%20Build&logo=android&logoColor=white&style=for-the-badge&color=588157" alt="Android Build Status"/>
<img src="https://img.shields.io/github/actions/workflow/status/comaps/comaps/.github/workflows/android-check.yaml?label=Android%20Build&logo=android&logoColor=white&style=for-the-badge" alt="Android Build Status"/>
</a>
<a href="https://github.com/comaps/comaps/actions/workflows/ios-check.yaml">
<img src="https://img.shields.io/github/actions/workflow/status/comaps/comaps/.github/workflows/ios-check.yaml?label=iOS%20Build&logo=apple&logoColor=white&style=for-the-badge&color=588157" alt="iOS Build Status"/>
<img src="https://img.shields.io/github/actions/workflow/status/comaps/comaps/.github/workflows/ios-check.yaml?label=iOS%20Build&logo=apple&logoColor=white&style=for-the-badge" alt="iOS Build Status"/>
</a>
<a href="https://opencollective.com/comaps">
<img src="https://img.shields.io/opencollective/all/comaps?label=Open%20Collective%20Donors&logo=opencollective&logoColor=white&style=for-the-badge&color=588157" alt="Open Collective Donors"/>

View File

@@ -0,0 +1 @@
CoMaps - Mapas ensin conexón con privacidá

View File

@@ -0,0 +1 @@
Лесна навигация - Открийте повече от вашето пътуване - Подкрепен от общността

View File

@@ -0,0 +1 @@
CoMaps - Хайкинг, Велосипед, Пътуване без Интернет

View File

@@ -1,10 +1,8 @@
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!
OpenStreetMap-Daten vom 28. Oktober
Aktualisierte Karten-Icons, inkl. Farben für Unterhaltungs-, Sport- & andere Unternehmen
Informationen zu Steckdosen an EV-Ladestationen
Symbole für Sportzentren, Veranstaltungsorte, Massagesalons, Gästehäuser und einige stillgelegte Unternehmen
Verbesserungen bei der Suche
Behebung eines Absturzes bei der Suche
Verbesserte Sprachführung während der Navigation (via OM)
Weitere Änderungen finden in unseren Codeberg-Versionshinweisen!

View File

@@ -1,10 +1,7 @@
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
Recategorized map icons including some new colors for entertainment, sports and other businesses
Display info about available sockets on charging stations
Added icons for different sport centres, event venues, massage salons, guest houses and some disused businesses
Multiple search improvements
Fixed crash in search
Improved voice guidance during navigation (via OM project)
Check our Codeberg release notes for more changes!

View File

@@ -1,10 +1,7 @@
• 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
• Datos OSM del 28/10
Iconos del mapa recategorizados, incluyendo nuevos colores
Visualización de información sobre enchufes disponibles en estaciones de recarga
Adición de iconos para diferentes centros deportivos, lugares de eventos, salones de masajes, posadas y algunos establecimientos comerciales desactivados
Varias mejoras y correcciones de errores en la búsqueda
Mejora en la orientación por voz durante la navegación (via OM)
Más detalles en Codeberg

View File

@@ -1,9 +1,8 @@
• 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
• Données OpenStreetMap du 28 octobre
Recatégorisation des icônes sur la carte avec ajout de nouvelles couleurs pour certains types de lieux
• Affichage des prises sur les bornes électriques
Ajout d'icônes pour les centres sportifs, salles d'événements, salon de massage et autres lieux
Multiple améliorations dans la recherche
Correction d'un plantage dans la recherche
Amélioration de la synthèse vocale durant la navigation (via le projet OM)
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,7 @@
• 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
• Dados OSM de 28/10
Ícones do mapa recategorizados, incluindo novas cores
• Exibição de informações sobre tomadas disponíveis em eletropostos
Adição de ícones para diferentes centros esportivos, locais de eventos, salões de massagem, pousadas e alguns estabelecimentos comerciais desativados
Diversas melhorias e correção de erro na busca
Melhoria na orientação por voz durante a navegação (via projeto OM)
Confira nossas notas de lançamento no Codeberg para mais detalhes!

View File

@@ -0,0 +1,32 @@
Uma aplicação pela comunidade, grátis e open-source, de mapas baseada em dados do OpenStreetMap e reforçada com compromisso para transparência, privacidade e sem fins lucrativos. CoMaps é um fork/spin-off de Organic Maps, que, por sua vez, é um fork de Maps.ME
Leia sobre as razões deste projeto e a sua direção em <b><i>codeberg.org/comaps</i></b>.
Junte-se à comunidade e ajude a fazer a melhor aplicação de mapas
• Use a aplicação e partilhe-a com outros
• Dê feedback e reporte problemas
• Atualize os dados de mapa na aplicação ou no site do OpenStreetMap
‣ <b>Simples e Polida</b>: funcionalidades essenciais fáceis que “somente funcionam”.
‣ <b>Foco Offline</b>: Planeie e navegue as suas viagens no estrangeiro sem dados móveis, procure locais numa caminhada distante, etc. Todas as funções da aplicação foram criadas com intenção de serem usadas sem internet.
‣ <b>Respeita a privacidade</b>: A aplicação foi criada com privacidade em mente — não identifica o utilizador, não rastreia, e não usa a sua informação pessoal. Sem anúncios.
‣ <b>Saves Your Battery and Space</b>: Não esgota a sua bateria ao contrário de outras aplicações. Mapas compactos salvam espaço no seu telemóvel.
‣ <b>Gratuita e Feita pela Comunidade</b>: Pessoas como si ajudam a criar a aplicação ao adicionar locais ao OpenStreetMap, testando e dando opiniões em funcionalidades e contribuindo com dotes de desenvolvimento e dinheiro.
‣ <b>Decisões e Finanças Abertas e Transparentes, Sem fins lucrativos e Open-Source.</b>
<b>Funcionalidades principais</b>:
• Mapas detalhados descarregáveis com locais que não estão disponíveis com o Google Maps
• Modo ao Ar Livre com trilhos de caminhada destacados, acampamentos, fontes de água, cumes, curvas de nível, etc
• Caminhos pedestres e ciclovias
• Pontos de interesse como restaurantes, estações de serviço, hotéis, lojas, atrações e muitos mais
• Pesquise por nome, endereço, ou por categoria de ponto de interesse
• Navegação com anúncios de voz ao caminhar, pedalar ou conduzir
• Marque os seus locais favoritos com um único clique
• Artigos da Wikipédia Offline
• Camada de metro e direções
• Gravação de Percursos
• Exportar e importar marcadores e percursos em formatos KML, KMZ, GPX
• Um modo escuro para usar durante a noite
• Melhore a informação do mapa para todos com um editor básico embebido
<b>A liberdade chegou</b>
Descubra a sua jornada, navegue o mundo com privacidade e a comunidade à frente!

View File

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

View File

@@ -1 +1 @@
Enostavna navigacija Odkrij več o svojem potovanju Podprto v skupnosti
Enostavno usmerjanje Odkrij več o svojem potovanju Podprto v skupnosti

View File

@@ -1,4 +1,4 @@
这是一个由社区主导、以 OpenStreetMap 数据为基础的免费开源地图应用建立在我们对运营透明、隐私安全和非营利性的承诺之上。CoMaps 是 Organic Maps 的分叉/衍生产品,而 Organic Maps 则是 Maps.ME 的分叉。
这是一个由社区主导、以 OpenStreetMap 数据为基础的自由开源地图应用建立在我们对运营透明、隐私安全和非营利性的承诺之上。CoMaps 是 Organic Maps 的分叉/衍生产品,而 Organic Maps 则是 Maps.ME 的分叉。
如需了解此项目诞生的原因及未来方向,请访问 <b><i>codeberg.org/comaps</i></b>。
加入以上社区,和大家一起打造最优质的地图应用
@@ -10,7 +10,7 @@
‣ <b>尊重隐私</b>:开发者们在设计 CoMaps 时优先考虑的是保护用户隐私。CoMaps 无法识别用户身份、无法跟踪用户活动也无法收集个人信息。此外CoMaps 不会也不能显示任何广告。
‣ <b>简洁精致</b>:轻便易用、不出差错的基本功能。
‣ <b>节省电池电量和空间</b>:不会像其他导航应用那样耗电。精简的地图可以节省宝贵的手机空间。
‣ <b>由社区合作创建的免费应用</b>:如同您一样的用户通过向 OpenStreetMap 添加地点、测试功能并提供反馈、无私地贡献自己的编程技能和资金,协力开发了 CoMaps。
‣ <b>自由且社区共建</b>:如同您一样的用户通过向 OpenStreetMap 添加地点、测试功能并提供反馈、无私地贡献自己的编程技能和资金,协力开发了 CoMaps。
‣ <b>决策问责、财务透明、非营利性、完全开源。</b>
<b>主要功能</b>
@@ -25,7 +25,7 @@
• 地铁交通图层和路线指示
• 轨迹记录
• 以 KML、KMZ 和 GPX 格式导出和导入书签和轨迹
选择天暗后自动开启的黑暗模式
深色模式,适配夜间使用场景
• 使用基本的内置编辑器来编辑 OpenStreetMap 地点,帮助大家改进地图数据
<b>自由在此</b>

View File

@@ -1 +1 @@
version: 2025.03.02-7-FDroid+25030207
version: 2025.11.01-7-FDroid+25110107

View File

@@ -0,0 +1 @@
Лесна навигация - Открийте повече от вашето пътуване - Подкрепен от общността

View File

@@ -0,0 +1 @@
CoMaps - Пътуване с Приватност

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

@@ -0,0 +1,36 @@
Uma aplicação pela comunidade, grátis e open-source, de mapas baseada em dados do OpenStreetMap e reforçada com compromisso para transparência, privacidade e sem fins lucrativos.
Junte-se à comunidade e ajude a fazer a melhor aplicação de mapas
• Use a aplicação e partilhe-a com outros
• Dê feedback e reporte problemas
• Atualize os dados de mapa na aplicação ou no site do OpenStreetMap
<i>O seu feedback e reviews de 5 estrelas são a melhor maneira de nos ajudar!</i>
‣ <b>Simples e Polida</b>: funcionalidades essenciais fáceis que “somente funcionam”.
‣ <b>Foco Offline</b>: Planeie e navegue as suas viagens no estrangeiro sem dados móveis, procure locais numa caminhada distante, etc. Todas as funções da aplicação foram criadas com intenção de serem usadas sem internet.
‣ <b>Respeita a privacidade</b>: A aplicação foi criada com privacidade em mente — não identifica o utilizador, não rastreia, e não usa a sua informação pessoal. Sem anúncios.
‣ <b>Saves Your Battery and Space</b>: Não esgota a sua bateria ao contrário de outras aplicações. Mapas compactos salvam espaço no seu telemóvel.
‣ <b>Gratuita e Feita pela Comunidade</b>: Pessoas como si ajudam a criar a aplicação ao adicionar locais ao OpenStreetMap, testando e dando opiniões em funcionalidades e contribuindo com dotes de desenvolvimento e dinheiro.
‣ <b>Decisões e Finanças Abertas e Transparentes, Sem fins lucrativos e Open-Source.</b>
<b>Funcionalidades principais</b>:
• Mapas detalhados descarregáveis com locais que não estão disponíveis com o Google Maps
• Modo ao Ar Livre com trilhos de caminhada destacados, acampamentos, fontes de água, cumes, curvas de nível, etc
• Caminhos pedestres e ciclovias
• Pontos de interesse como restaurantes, estações de serviço, hotéis, lojas, atrações e muitos mais
• Pesquise por nome, endereço, ou por categoria de ponto de interesse
• Navegação com anúncios de voz ao caminhar, pedalar ou conduzir
• Marque os seus locais favoritos com um único clique
• Artigos da Wikipédia Offline
• Camada de metro e direções
• Gravação de Percursos
• Exportar e importar marcadores e percursos em formatos KML, KMZ, GPX
• Um modo escuro para usar durante a noite
• Melhore a informação do mapa para todos com um editor básico embebido
• Suporte para Android Auto
Por favor, reporte problemas da aplicação, sugira ideias e junte-se à nossa comunidade no website <b><i>comaps.app</i></b>.
<b>A liberdade chegou</b>
Descubra a sua jornada, navegue o mundo com privacidade e a comunidade à frente!

View File

@@ -0,0 +1,36 @@
Skupnostno vodena brezplačna in odprtokodna aplikacija za zemljevide, ki temelji na podatkih OpenStreetMap, ter je okrepljena z zavezanostjo k transparentnosti, zasebnosti, in ostajanju neprofitne organizacije.
Pridružite se skupnosti in pomagajte ustvariti najboljšo aplikacijo za zemljevide.
• Uporabljajte aplikacijo in jo priporočajte drugim.
• Podajte povratne informacije in poročajte o težavah.
• Posodobite podatke zemljevida v aplikaciji ali na spletni strani OpenStreetMap.
<i>Vaše povratne informacije in 5-zvezdične ocene so najboljša podpora za nas!</i>
‣ <b>Preprostost in izpopolnjenost</b>: bistvene, enostavne za uporabo funkcije, ki preprosto delujejo.
‣ <b>Osredotočena na delovanje brez internetne povezave</b>: načrtujte in navigirajte svoje potovanje v tujini brez potrebe po mobilni povezavi, iščite točke na poti med daljšo pohodniško turo, itd. Vse funkcije aplikacije so zasnovane za delovanje brez internetne povezave.
‣ <b>Spoštovanje zasebnosti</b>: aplikacija je zasnovana z mislijo na zasebnost ne identificira ljudi, ne sledi in ne zbira osebnih podatkov. Brez oglasov.
‣ <b>Varčuje z baterijo in prostorom</b>: ne izčrpava baterije kot druge navigacijske aplikacije. Kompaktni zemljevidi varčujejo dragoceni prostor na vašem telefonu.
‣ <b>Brezplačna in ustvarjena s pomočjo skupnosti</b>: ljudje, kot ste vi, so pomagali ustvariti aplikacijo z dodajanjem krajev v OpenStreetMap, testiranjem in dajanjem povratnih informacij o funkcijah ter prispevanjem svojih razvojnih veščin in denarja.
‣ <b>Odprto in pregledno odločanje in finance, neprofitna in popolnoma odprtokodna aplikacija.
<b>Glavne značilnosti</b>:
• Podrobni zemljevidi z mesti, ki niso na voljo v Google Maps, ki jih lahko prenesete
• Način za uporabo na prostem z označenimi pohodniškimi potmi, kampi, vodnimi viri, vrhovi, višinskimi krivuljami itd.
• Pešpoti in kolesarske poti
• Zanimivosti, kot so restavracije, bencinske črpalke, hoteli, trgovine, znamenitosti in še veliko več
• Iskanje po imenu, naslovu ali kategoriji zanimivih točk
• Navigacija z glasovnimi napovedmi za hojo, kolesarjenje ali vožnjo
• Z enim dotikom dodajte svoje priljubljene kraje v zaznamke
• Članki iz Wikipedije za uporabo brez internetne povezave
• Plast podzemne železnice in navodila za pot
• Sledenje poti
• Izvoz in uvoz zaznamkov in poti v formatih KML, KMZ, GPX
• Temni način za uporabo ponoči
• Izboljšajte zemljevidne podatke za vse z uporabo vgrajenega osnovnega urejevalnika.
• Podpora za Android Auto.
Prijavite težave z aplikacijo, predlagajte ideje in se pridružite naši skupnosti na spletni strani <b><i>comaps.app</i></b>.
<b>Svoboda je tu</b>
Odkrijte svojo pot, raziskujte svet z zasebnostjo in skupnostjo v ospredju!

View File

@@ -1 +1 @@
Enostavna navigacija Odkrij več o svojem potovanju Podprto v skupnosti
Enostavno usmerjanje Odkrij več o svojem potovanju Podprto v skupnosti

View File

@@ -0,0 +1 @@
CoMaps - Usmerjajte zasebno

View File

@@ -1,4 +1,4 @@
这是一个由社区主导、以 OpenStreetMap 数据为基础的免费开源地图应用,建立在我们对运营透明、隐私安全和非营利性的承诺之上。
这是一个由社区主导、以 OpenStreetMap 数据为基础的自由开源地图应用,建立在我们对运营透明、隐私安全和非营利性的承诺之上。
加入社区,和大家一起打造最优质的地图应用
• 使用 CoMaps 的同时也分享推荐给周围的人
@@ -11,7 +11,7 @@
‣ <b>以提供离线服务为核心</b>:无需移动网络即可规划和导航您的海外旅行,郊外远足时仍可搜索航点等等。所有功能均可离线使用。
‣ <b>尊重隐私</b>:开发者们在设计 CoMaps 时优先考虑的是保护用户隐私。CoMaps 无法识别用户身份、无法跟踪用户活动也无法收集个人信息。此外CoMaps 不会也不能显示任何广告。
‣ <b>节省电池电量和空间</b>:不会像其他导航应用那样耗电。精简的地图可以节省宝贵的手机空间。
‣ <b>由社区合作创建的免费应用</b>:如同您一样的用户通过向 OpenStreetMap 添加地点、测试功能并提供反馈、无私地贡献自己的编程技能和资金,协力开发了 CoMaps。
‣ <b>自由且社区共建</b>:如同您一样的用户通过向 OpenStreetMap 添加地点、测试功能并提供反馈、无私地贡献自己的编程技能和资金,协力开发了 CoMaps。
‣ <b>决策问责、财务透明、非营利性、完全开源。</b>
<b>主要功能</b>
@@ -26,7 +26,7 @@
• 地铁交通图层和路线指示
• 轨迹记录
• 以 KML、KMZ 和 GPX 格式导出和导入书签和轨迹
选择天暗后自动开启的黑暗模式
深色模式,适配夜间使用场景
• 使用基本的内置编辑器来编辑 OpenStreetMap 地点,帮助大家改进地图数据
• 支持 Android Auto

View File

@@ -89,6 +89,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 +350,7 @@
<activity
android:name="app.organicmaps.DownloadResourcesLegacyActivity"
android:theme="@style/MwmTheme.DownloadResourcesLegacy"
android:configChanges="orientation|screenLayout|screenSize"
android:screenOrientation="fullUser"/>
@@ -365,6 +367,7 @@
<activity
android:name="app.organicmaps.MwmActivity"
android:theme="@style/MwmTheme.MainActivity"
android:launchMode="singleTask"
android:configChanges="uiMode"
android:screenOrientation="fullUser"
@@ -386,6 +389,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 +416,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 +425,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

@@ -7,23 +7,17 @@ import androidx.annotation.NonNull;
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();
}
protected int getStyle()
{
return STYLE_NORMAL;
}
protected @StyleRes int getCustomTheme()
@StyleRes
protected int getCustomTheme()
{
return 0;
}
@@ -40,18 +34,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

@@ -188,7 +188,7 @@ public class BookmarkCategoriesFragment extends BaseMwmRecyclerFragment<Bookmark
ArrayList<MenuBottomSheetItem> items = new ArrayList<>();
if (mSelectedCategory != null)
{
items.add(new MenuBottomSheetItem(R.string.edit, R.drawable.ic_settings,
items.add(new MenuBottomSheetItem(R.string.edit, R.drawable.ic_edit,
() -> onSettingsActionSelected(mSelectedCategory)));
items.add(new MenuBottomSheetItem(mSelectedCategory.isVisible() ? R.string.hide : R.string.show,
mSelectedCategory.isVisible() ? R.drawable.ic_hide : R.drawable.ic_show,

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

@@ -466,12 +466,10 @@ public class BookmarkListAdapter extends RecyclerView.Adapter<Holders.BaseBookma
View desc = inflater.inflate(R.layout.item_category_description, parent, false);
MaterialTextView moreBtn = desc.findViewById(R.id.more_btn);
MaterialTextView text = desc.findViewById(R.id.text);
MaterialTextView title = desc.findViewById(R.id.title);
setMoreButtonVisibility(text, moreBtn);
holder = new Holders.DescriptionViewHolder(desc, mSectionsDataSource.getCategory());
text.setOnClickListener(v -> onMoreButtonClicked(text, moreBtn));
moreBtn.setOnClickListener(v -> onMoreButtonClicked(text, moreBtn));
title.setOnClickListener(v -> onMoreButtonClicked(text, moreBtn));
break;
}

View File

@@ -282,11 +282,11 @@ public class BookmarksListFragment extends BaseMwmRecyclerFragment<ConcatAdapter
{
if (isEmptySearchResults())
{
requirePlaceholder().setContent(R.string.search_not_found, R.string.search_not_found_query);
requirePlaceholder().setContent(R.string.search_not_found, R.string.search_not_found_query, R.drawable.ic_search_fail);
}
else if (isEmpty())
{
requirePlaceholder().setContent(R.string.bookmarks_empty_list_title, R.string.bookmarks_empty_list_message);
requirePlaceholder().setContent(R.string.bookmarks_empty_list_title, R.string.bookmarks_empty_list_message, R.drawable.ic_bookmarks);
}
boolean isEmptyRecycler = isEmpty() || isEmptySearchResults();
@@ -771,7 +771,7 @@ public class BookmarksListFragment extends BaseMwmRecyclerFragment<ConcatAdapter
items.add(new MenuBottomSheetItem(R.string.export_file_gpx, R.drawable.ic_file_gpx,
() -> onShareOptionSelected(KmlFileType.Gpx)));
}
items.add(new MenuBottomSheetItem(R.string.edit, R.drawable.ic_settings, this::onSettingsOptionSelected));
items.add(new MenuBottomSheetItem(R.string.edit, R.drawable.ic_edit, this::onSettingsOptionSelected));
if (!isLastOwnedCategory())
items.add(new MenuBottomSheetItem(R.string.delete_list, R.drawable.ic_delete, this::onDeleteOptionSelected));
return items;

View File

@@ -438,21 +438,17 @@ public class Holders
static final float SPACING_MULTIPLE = 1.0f;
static final float SPACING_ADD = 0.0f;
@NonNull
private final MaterialTextView mTitle;
@NonNull
private final MaterialTextView mDescText;
DescriptionViewHolder(@NonNull View itemView, @NonNull BookmarkCategory category)
{
super(itemView);
mDescText = itemView.findViewById(R.id.text);
mTitle = itemView.findViewById(R.id.title);
}
@Override
void bind(@NonNull SectionPosition position, @NonNull BookmarkListAdapter.SectionsDataSource sectionsDataSource)
{
mTitle.setText(sectionsDataSource.getCategory().getName());
bindDescription(sectionsDataSource.getCategory());
}
@@ -462,9 +458,12 @@ public class Holders
String formattedDesc = desc.replace("\n", "<br>");
Spanned spannedDesc = Utils.fromHtml(formattedDesc);
mDescText.setText(spannedDesc);
UiUtils.showIf(!TextUtils.isEmpty(spannedDesc), mDescText);
if (!TextUtils.isEmpty(spannedDesc)) {
mDescText.setText(spannedDesc);
}
else {
mDescText.setText(R.string.list_description_empty);
}
}
}
}

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

@@ -222,10 +222,10 @@ public class DownloaderFragment
return;
if (mAdapter != null && mAdapter.isSearchResultsMode())
placeholder.setContent(R.string.search_not_found, R.string.search_not_found_query);
placeholder.setContent(R.string.search_not_found, R.string.search_not_found_query, R.drawable.ic_search_fail);
else
placeholder.setContent(R.string.downloader_no_downloaded_maps_title,
R.string.downloader_no_downloaded_maps_message);
R.string.downloader_no_downloaded_maps_message, R.drawable.ic_download);
}
@Override

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,15 @@ 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());
}
// Hide socket until https://codeberg.org/comaps/comaps/issues/2368 is fixed
//UiUtils.showIf(hasChargeSockets, mCardChargingStation);
setCardVisibility(mCardDetails, mDetailsBlocks, editableDetails);
setCardVisibility(mCardSocialMedia, mSocialMediaBlocks, editableDetails);
}
@@ -351,6 +377,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 +738,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 +811,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 +1008,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

@@ -0,0 +1,208 @@
package app.organicmaps.editor;
import android.content.res.Configuration;
import android.content.res.Resources;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import com.google.android.material.timepicker.MaterialTimePicker;
import com.google.android.material.timepicker.TimeFormat;
import app.organicmaps.R;
import app.organicmaps.sdk.editor.data.HoursMinutes;
import app.organicmaps.sdk.util.DateUtils;
public class FromToTimePicker
{
private final FragmentActivity mActivity;
private final FragmentManager mFragmentManager;
private final OnPickListener mListener;
private final int mId;
private final boolean mIs24HourFormat;
private final Resources mResources;
private HoursMinutes mFromTime;
private HoursMinutes mToTime;
private MaterialTimePicker mToTimePicker;
private MaterialTimePicker mFromTimePicker;
private boolean mIsFromTimePicked;
private int mInputMode;
public static void pickTime(@NonNull Fragment fragment,
@NonNull FromToTimePicker.OnPickListener listener,
@NonNull HoursMinutes fromTime,
@NonNull HoursMinutes toTime,
int id,
boolean startWithToTime)
{
FromToTimePicker timePicker = new FromToTimePicker(fragment,
listener,
fromTime,
toTime,
id);
if (startWithToTime)
timePicker.showToTimePicker();
else
timePicker.showFromTimePicker();
}
private FromToTimePicker(@NonNull Fragment fragment,
@NonNull FromToTimePicker.OnPickListener listener,
@NonNull HoursMinutes fromTime,
@NonNull HoursMinutes toTime,
int id)
{
mActivity = fragment.requireActivity();
mFragmentManager = fragment.getChildFragmentManager();
mListener = listener;
mFromTime = fromTime;
mToTime = toTime;
mId = id;
mIsFromTimePicked = false;
mInputMode = MaterialTimePicker.INPUT_MODE_CLOCK;
mIs24HourFormat = DateUtils.is24HourFormat(mActivity);
mResources = mActivity.getResources();
mActivity.addOnConfigurationChangedListener(this::handleConfigurationChanged);
}
public void showFromTimePicker()
{
if (mFromTimePicker != null)
{
saveState(mFromTimePicker, true);
mFromTimePicker.dismiss();
}
mFromTimePicker = buildFromTimePicker();
mFromTimePicker.show(mFragmentManager, null);
}
public void showToTimePicker()
{
if (mToTimePicker != null)
{
saveState(mToTimePicker, false);
mToTimePicker.dismiss();
}
mToTimePicker = buildToTimePicker();
mToTimePicker.show(mFragmentManager, null);
}
private MaterialTimePicker buildFromTimePicker()
{
MaterialTimePicker timePicker = buildTimePicker(mFromTime,
mResources.getString(R.string.editor_time_from),
mResources.getString(R.string.next_button),
null);
timePicker.addOnNegativeButtonClickListener(view -> finishTimePicking(false));
timePicker.addOnPositiveButtonClickListener(view ->
{
mIsFromTimePicked = true;
saveState(timePicker, true);
mFromTimePicker = null;
showToTimePicker();
});
timePicker.addOnCancelListener(view -> finishTimePicking(false));
return timePicker;
}
private MaterialTimePicker buildToTimePicker()
{
MaterialTimePicker timePicker = buildTimePicker(mToTime,
mResources.getString(R.string.editor_time_to),
null,
mResources.getString(R.string.back));
timePicker.addOnNegativeButtonClickListener(view ->
{
saveState(timePicker, false);
mToTimePicker = null;
if (mIsFromTimePicked)
showFromTimePicker();
else
finishTimePicking(false);
});
timePicker.addOnPositiveButtonClickListener(view ->
{
saveState(timePicker, false);
finishTimePicking(true);
});
timePicker.addOnCancelListener(view -> finishTimePicking(false));
return timePicker;
}
@NonNull
private MaterialTimePicker buildTimePicker(@NonNull HoursMinutes time,
@NonNull String title,
@Nullable String positiveButtonTextOverride,
@Nullable String negativeButtonTextOverride)
{
MaterialTimePicker.Builder builder = new MaterialTimePicker.Builder()
.setTitleText(title)
.setTimeFormat(mIs24HourFormat ? TimeFormat.CLOCK_24H : TimeFormat.CLOCK_12H)
.setInputMode(mInputMode)
.setTheme(R.style.MwmMain_MaterialTimePicker)
.setHour((int) time.hours)
.setMinute((int) time.minutes);
if (positiveButtonTextOverride != null)
builder.setPositiveButtonText(positiveButtonTextOverride);
if (negativeButtonTextOverride != null)
builder.setNegativeButtonText(negativeButtonTextOverride);
return builder.build();
}
private void saveState(@NonNull MaterialTimePicker timePicker, boolean isFromTime)
{
mInputMode = timePicker.getInputMode();
if (isFromTime)
mFromTime = getHoursMinutes(timePicker);
else
mToTime = getHoursMinutes(timePicker);
}
private HoursMinutes getHoursMinutes(@NonNull MaterialTimePicker timePicker)
{
return new HoursMinutes(timePicker.getHour(), timePicker.getMinute(), mIs24HourFormat);
}
private void finishTimePicking(boolean isConfirmed)
{
mActivity.removeOnConfigurationChangedListener(this::handleConfigurationChanged);
if (isConfirmed)
mListener.onHoursMinutesPicked(mFromTime, mToTime, mId);
}
private void handleConfigurationChanged(Configuration configuration)
{
if (mFromTimePicker != null && mFromTimePicker.isVisible())
showFromTimePicker();
else if (mToTimePicker != null && mToTimePicker.isVisible())
showToTimePicker();
}
public interface OnPickListener
{
void onHoursMinutesPicked(HoursMinutes from, HoursMinutes to, int id);
}
}

View File

@@ -1,216 +0,0 @@
package app.organicmaps.editor;
import android.annotation.SuppressLint;
import android.app.Dialog;
import android.content.Context;
import android.content.res.ColorStateList;
import android.os.Bundle;
import android.text.format.DateFormat;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
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;
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;
import com.google.android.material.textview.MaterialTextView;
public class HoursMinutesPickerFragment extends BaseMwmDialogFragment
{
private static final String EXTRA_FROM = "HoursMinutesFrom";
private static final String EXTRA_TO = "HoursMinutesTo";
private static final String EXTRA_SELECT_FIRST = "SelectedTab";
private static final String EXTRA_ID = "Id";
public static final int TAB_FROM = 0;
public static final int TAB_TO = 1;
private HoursMinutes mFrom;
private HoursMinutes mTo;
private TimePicker mPicker;
private View mPickerHoursLabel;
@IntRange(from = 0, to = 1)
private int mSelectedTab;
private TabLayout mTabs;
private int mId;
private Button mOkButton;
public interface OnPickListener
{
void onHoursMinutesPicked(HoursMinutes from, HoursMinutes to, int id);
}
public static void pick(Context context, FragmentManager manager, @NonNull HoursMinutes from,
@NonNull HoursMinutes to, @IntRange(from = 0, to = 1) int selectedPosition, int id)
{
final Bundle args = new Bundle();
args.putParcelable(EXTRA_FROM, from);
args.putParcelable(EXTRA_TO, to);
args.putInt(EXTRA_SELECT_FIRST, selectedPosition);
args.putInt(EXTRA_ID, id);
final HoursMinutesPickerFragment fragment = (HoursMinutesPickerFragment) manager.getFragmentFactory().instantiate(
context.getClassLoader(), HoursMinutesPickerFragment.class.getName());
fragment.setArguments(args);
fragment.show(manager, null);
}
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState)
{
readArgs();
final View root = createView();
// 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();
dialog.setOnShowListener(dialogInterface -> {
mOkButton = dialog.getButton(AlertDialog.BUTTON_POSITIVE);
mOkButton.setOnClickListener(v -> {
if (mSelectedTab == TAB_FROM)
{
// noinspection ConstantConditions
mTabs.getTabAt(TAB_TO).select();
return;
}
saveHoursMinutes();
dismiss();
if (getParentFragment() instanceof OnPickListener)
((OnPickListener) getParentFragment()).onHoursMinutesPicked(mFrom, mTo, mId);
});
refreshPicker();
});
return dialog;
}
private void readArgs()
{
final Bundle args = getArguments();
if (args == null)
throw new IllegalArgumentException("Args must not be null");
mFrom = Utils.getParcelable(args, EXTRA_FROM, HoursMinutes.class);
mTo = Utils.getParcelable(args, EXTRA_TO, HoursMinutes.class);
mSelectedTab = args.getInt(EXTRA_SELECT_FIRST);
mId = args.getInt(EXTRA_ID);
}
private View createView()
{
final LayoutInflater inflater = LayoutInflater.from(requireActivity());
@SuppressLint("InflateParams")
final View root = inflater.inflate(R.layout.fragment_timetable_picker, null);
mPicker = root.findViewById(R.id.picker);
mPicker.setIs24HourView(DateFormat.is24HourFormat(requireActivity()));
@SuppressLint("DiscouragedApi")
int id = getResources().getIdentifier("hours", "id", "android");
if (id != 0)
{
mPickerHoursLabel = mPicker.findViewById(id);
if (!(mPickerHoursLabel instanceof TextView))
mPickerHoursLabel = null;
}
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);
tabView.setTextColor(textColor);
mTabs.addTab(mTabs.newTab().setCustomView(tabView), true);
tabView = (MaterialTextView) inflater.inflate(R.layout.tab_timepicker, mTabs, false);
tabView.setText(getResources().getString(R.string.editor_time_to));
tabView.setTextColor(textColor);
mTabs.addTab(mTabs.newTab().setCustomView(tabView), true);
mTabs.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab)
{
if (!isInit())
return;
saveHoursMinutes();
mSelectedTab = tab.getPosition();
refreshPicker();
if (mPickerHoursLabel != null)
mPickerHoursLabel.performClick();
}
@Override
public void onTabUnselected(TabLayout.Tab tab)
{}
@Override
public void onTabReselected(TabLayout.Tab tab)
{}
});
return root;
}
private void saveHoursMinutes()
{
boolean is24HourFormat = DateUtils.is24HourFormat(requireContext());
final HoursMinutes hoursMinutes =
new HoursMinutes(mPicker.getCurrentHour(), mPicker.getCurrentMinute(), is24HourFormat);
if (mSelectedTab == TAB_FROM)
mFrom = hoursMinutes;
else
mTo = hoursMinutes;
}
private boolean isInit()
{
return mOkButton != null && mPicker != null;
}
private void refreshPicker()
{
if (!isInit())
return;
HoursMinutes hoursMinutes;
int okBtnRes;
if (mSelectedTab == TAB_FROM)
{
hoursMinutes = mFrom;
okBtnRes = R.string.next_button;
}
else
{
hoursMinutes = mTo;
okBtnRes = R.string.ok;
}
mPicker.setCurrentMinute((int) hoursMinutes.minutes);
mPicker.setCurrentHour((int) hoursMinutes.hours);
mOkButton.setText(okBtnRes);
}
}

View File

@@ -113,9 +113,6 @@ public class PhoneListAdapter extends RecyclerView.Adapter<PhoneListAdapter.View
deleteButton = itemView.findViewById(R.id.delete_icon);
deleteButton.setOnClickListener(this);
// TODO: setting icons from code because icons defined in layout XML are white.
deleteButton.setImageResource(R.drawable.ic_delete);
((ShapeableImageView) itemView.findViewById(R.id.phone_icon)).setImageResource(R.drawable.ic_phone);
}
public void setPosition(int position)

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

@@ -6,6 +6,7 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.CompoundButton;
import android.widget.LinearLayout;
import androidx.annotation.IdRes;
import androidx.annotation.IntRange;
import androidx.annotation.Nullable;
@@ -29,13 +30,13 @@ import java.util.Calendar;
import java.util.List;
class SimpleTimetableAdapter extends RecyclerView.Adapter<SimpleTimetableAdapter.BaseTimetableViewHolder>
implements HoursMinutesPickerFragment.OnPickListener, TimetableProvider
implements FromToTimePicker.OnPickListener, TimetableProvider
{
private static final int TYPE_TIMETABLE = 0;
private static final int TYPE_ADD_TIMETABLE = 1;
private static final int ID_OPENING = 0;
private static final int ID_CLOSING = 1;
private static final int ID_OPENING_TIME = 0;
private static final int ID_CLOSED_SPAN = 1;
private static final int[] DAYS = {R.id.day1, R.id.day2, R.id.day3, R.id.day4, R.id.day5, R.id.day6, R.id.day7};
@@ -69,7 +70,7 @@ class SimpleTimetableAdapter extends RecyclerView.Adapter<SimpleTimetableAdapter
@Override
public String getTimetables()
{
return OpeningHours.nativeTimetablesToString(mItems.toArray(new Timetable[mItems.size()]));
return OpeningHours.nativeTimetablesToString(mItems.toArray(new Timetable[0]));
}
@Override
@@ -101,7 +102,7 @@ class SimpleTimetableAdapter extends RecyclerView.Adapter<SimpleTimetableAdapter
private void addTimetable()
{
mItems.add(OpeningHours.nativeGetComplementTimetable(mItems.toArray(new Timetable[mItems.size()])));
mItems.add(OpeningHours.nativeGetComplementTimetable(mItems.toArray(new Timetable[0])));
notifyItemInserted(mItems.size() - 1);
refreshComplement();
}
@@ -115,25 +116,31 @@ class SimpleTimetableAdapter extends RecyclerView.Adapter<SimpleTimetableAdapter
private void refreshComplement()
{
mComplementItem = OpeningHours.nativeGetComplementTimetable(mItems.toArray(new Timetable[mItems.size()]));
mComplementItem = OpeningHours.nativeGetComplementTimetable(mItems.toArray(new Timetable[0]));
notifyItemChanged(getItemCount() - 1);
}
private void pickTime(int position,
@IntRange(from = HoursMinutesPickerFragment.TAB_FROM, to = HoursMinutesPickerFragment.TAB_TO)
int tab, @IntRange(from = ID_OPENING, to = ID_CLOSING) int id)
@IntRange(from = ID_OPENING_TIME, to = ID_CLOSED_SPAN) int id,
boolean startWithToTime)
{
final Timetable data = mItems.get(position);
mPickingPosition = position;
HoursMinutesPickerFragment.pick(mFragment.requireActivity(), mFragment.getChildFragmentManager(),
data.workingTimespan.start, data.workingTimespan.end, tab, id);
FromToTimePicker.pickTime(mFragment,
this,
data.workingTimespan.start,
data.workingTimespan.end,
id,
startWithToTime);
}
@Override
public void onHoursMinutesPicked(HoursMinutes from, HoursMinutes to, int id)
{
final Timetable item = mItems.get(mPickingPosition);
if (id == ID_OPENING)
if (id == ID_OPENING_TIME)
mItems.set(mPickingPosition, OpeningHours.nativeSetOpeningTime(item, new Timespan(from, to)));
else
mItems.set(mPickingPosition, OpeningHours.nativeAddClosedSpan(item, new Timespan(from, to)));
@@ -148,7 +155,7 @@ class SimpleTimetableAdapter extends RecyclerView.Adapter<SimpleTimetableAdapter
private void addWorkingDay(int day, int position)
{
final Timetable[] tts = mItems.toArray(new Timetable[mItems.size()]);
final Timetable[] tts = mItems.toArray(new Timetable[0]);
mItems = new ArrayList<>(Arrays.asList(OpeningHours.nativeAddWorkingDay(tts, position, day)));
refreshComplement();
notifyDataSetChanged();
@@ -156,7 +163,7 @@ class SimpleTimetableAdapter extends RecyclerView.Adapter<SimpleTimetableAdapter
private void removeWorkingDay(int day, int position)
{
final Timetable[] tts = mItems.toArray(new Timetable[mItems.size()]);
final Timetable[] tts = mItems.toArray(new Timetable[0]);
mItems = new ArrayList<>(Arrays.asList(OpeningHours.nativeRemoveWorkingDay(tts, position, day)));
refreshComplement();
notifyDataSetChanged();
@@ -262,13 +269,13 @@ class SimpleTimetableAdapter extends RecyclerView.Adapter<SimpleTimetableAdapter
{
final int id = v.getId();
if (id == R.id.time_open)
pickTime(getBindingAdapterPosition(), HoursMinutesPickerFragment.TAB_FROM, ID_OPENING);
pickTime(getBindingAdapterPosition(), ID_OPENING_TIME, false);
else if (id == R.id.time_close)
pickTime(getBindingAdapterPosition(), HoursMinutesPickerFragment.TAB_TO, ID_OPENING);
pickTime(getBindingAdapterPosition(), ID_OPENING_TIME, true);
else if (id == R.id.tv__remove_timetable)
removeTimetable(getBindingAdapterPosition());
else if (id == R.id.tv__add_closed)
pickTime(getBindingAdapterPosition(), HoursMinutesPickerFragment.TAB_FROM, ID_CLOSING);
pickTime(getBindingAdapterPosition(), ID_CLOSED_SPAN, false);
else if (id == R.id.allday)
swAllday.toggle();
}

View File

@@ -8,10 +8,9 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import app.organicmaps.R;
import app.organicmaps.base.BaseMwmRecyclerFragment;
import app.organicmaps.sdk.editor.data.HoursMinutes;
public class SimpleTimetableFragment extends BaseMwmRecyclerFragment<SimpleTimetableAdapter>
implements TimetableProvider, HoursMinutesPickerFragment.OnPickListener
implements TimetableProvider
{
private SimpleTimetableAdapter mAdapter;
@Nullable
@@ -57,10 +56,4 @@ public class SimpleTimetableFragment extends BaseMwmRecyclerFragment<SimpleTimet
{
mInitTimetables = timetables;
}
@Override
public void onHoursMinutesPicked(HoursMinutes from, HoursMinutes to, int id)
{
mAdapter.onHoursMinutesPicked(from, to, id);
}
}

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

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

@@ -273,7 +273,7 @@ public class SearchFragment extends BaseMwmFragment implements SearchListener, C
RecyclerView mResults = mResultsFrame.findViewById(R.id.recycler);
setRecyclerScrollListener(mResults);
mResultsPlaceholder = mResultsFrame.findViewById(R.id.placeholder);
mResultsPlaceholder.setContent(R.string.search_not_found, R.string.search_not_found_query);
mResultsPlaceholder.setContent(R.string.search_not_found, R.string.search_not_found_query, R.drawable.ic_search_fail);
mSearchAdapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver()
{

View File

@@ -47,7 +47,7 @@ public class SearchHistoryFragment extends BaseMwmRecyclerFragment<SearchHistory
super.onViewCreated(view, savedInstanceState);
getRecyclerView().setLayoutManager(new LinearLayoutManager(view.getContext()));
mPlaceHolder = view.findViewById(R.id.placeholder);
mPlaceHolder.setContent(R.string.search_history_title, R.string.search_history_text);
mPlaceHolder.setContent(R.string.search_history_title, R.string.search_history_text, R.drawable.ic_search_recent);
getAdapter().registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
@Override

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

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