Compare commits

..

217 Commits

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

Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-10-20 22:38:29 +03:00
mvglasow
c1340a9941 [traffic] Truncate locations which overshoot their endpoints
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-10-19 22:39:06 +03:00
mvglasow
a313526aed [routing] Documentation
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-10-19 18:02:04 +03:00
mvglasow
32d1a3a36e [traffic] Eliminate any ETA calculations, always return weight
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-10-19 17:59:48 +03:00
mvglasow
3a45740884 [routing] Documentation
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-10-19 17:45:35 +03:00
mvglasow
4e624bd04b [traffic] Comment out unused argument
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-10-19 17:45:25 +03:00
mvglasow
a3d1ed83c3 [routing][traffic] Different routing options in navigation and decoder mode
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-10-18 16:34:32 +03:00
mvglasow
38dbad0f7e [traffic] Add test cases
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-10-18 15:24:00 +03:00
mvglasow
7fe5823140 [traffic] Optimize offroad penalty in decoder
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-10-17 21:09:54 +03:00
mvglasow
091d510ba1 [traffic] Simplify ferry landing penalty in TraFF decoder
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-10-14 21:52:12 +03:00
mvglasow
f07c8d66d8 [traffic] Calculate segment weight based on road ref
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-10-14 21:41:22 +03:00
Yannik Bloscheck
c9f50cdc72 [ios] Enabled traffic button
Signed-off-by: Yannik Bloscheck <git@yannikbloscheck.com>
2025-10-14 10:54:37 +02:00
mvglasow
2ed9bc1880 [traffic] Properly process message replacement
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-10-12 16:39:44 +03:00
mvglasow
d098ecae15 [traff_assessment_tool] Ensure traffic layer is enabled
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-10-11 19:02:43 +03:00
mvglasow
9e06ec815e [traff_assessment_tool] Handle invalid selections gracefully, clear marks
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-10-11 15:08:17 +03:00
mvglasow
bf6cf27f8c [traff_assessment_tool] Clear markers when updating TraFF data
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-10-11 14:47:41 +03:00
mvglasow
bf36c875d5 Merge commit 'eb264889e3' into traffic 2025-10-10 18:40:35 +03:00
mvglasow
30b2df89cd [routing] Documentation
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-10-09 20:44:33 +03:00
mvglasow
ba2d653a30 [3party/protobuf] Fix faulty merge
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-09-28 13:55:34 +03:00
mvglasow
d6eacd7364 [drape] Correctly render traffic at zoom 20
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-09-26 22:53:08 +03:00
mvglasow
07c75e627e [traff_assessment_tool] Move to tools
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-09-12 21:40:35 +03:00
mvglasow
e7bde9aa05 [openlr_decoder] Fix faulty merge
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-09-12 21:27:21 +03:00
mvglasow
5ba708caff [map] Documentation
Signed-off-by: mvglasow <michael -at- vonglasow.com>
2025-09-12 19:06:21 +03:00
mvglasow
48e8f32e01 [traff_assessment_tool] Use different marker colors for reference points
Also fixes bug with DebugMarkPoint no longer rendering after the last merge

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

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

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

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

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

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

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

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

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

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

View File

@@ -1,501 +0,0 @@
name: map-generator
on:
workflow_dispatch: # Manual trigger
inputs:
jobs:
description: 'Which job(s) to run right now?'
required: true
default: 'all-except-upload'
type: choice
options:
- all-except-upload
- copy-coasts
- planet
- wiki
- isolines
- subways
- tiger
- maps
- upload
map-generator-continue:
description: 'Continue previous map generation?'
required: false
default: false
type: boolean
map-generator-countries:
description: 'Generate specific MWMs? (i.e. "US_New York_*, foo")'
required: false
type: string
reset:
description: 'Reset part of the system?'
required: false
default: 'no'
type: choice
options:
- 'no'
- wiki-ratelimit
## RCLONE_CONF is multi-line text containing keys and credentials for us2,ru1,fi1,de1 servers
env:
RCLONE_CONF: ${{ secrets.RCLONE_CONF }}
WIKIMEDIA_USERNAME: ${{ secrets.WIKIMEDIA_USERNAME }}
WIKIMEDIA_PASSWORD: ${{ secrets.WIKIMEDIA_PASSWORD }}
ZULIP_BOT_EMAIL: ${{ secrets.ZULIP_BOT_EMAIL }}
ZULIP_API_KEY: ${{ secrets.ZULIP_API_KEY }}
MWMCONTINUE: ${{ inputs.map-generator-continue }}
MWMCOUNTRIES: ${{ inputs.map-generator-countries }}
DEBIAN_FRONTEND: noninteractive
TZ: Etc/UTC
jobs:
clone-repos:
name: Clone Git Repos
runs-on: mapfilemaker
container:
image: codeberg.org/comaps/maps_generator:f6d53d54f794
volumes:
- /mnt/4tbexternal:/mnt/4tbexternal
concurrency:
group: ${{ github.workflow }}-map-generator-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
steps:
- uses: actions/cache@v4
with:
path: "~"
key: cache-${{ github.run_id }}-${{ github.run_attempt }}
- name: Checkout main repo
shell: bash
run: |
echo "Cloning $FORGEJO_SERVER_URL/$FORGEJO_REPOSITORY branch $FORGEJO_REF_NAME"
cd ~
git clone --recurse-submodules --shallow-submodules -b $FORGEJO_REF_NAME --single-branch $FORGEJO_SERVER_URL/$FORGEJO_REPOSITORY.git comaps
- name: Checkout wikiparser repo
shell: bash
run: |
cd ~
git clone https://codeberg.org/comaps/wikiparser.git
- name: Checkout subways repo
shell: bash
run: |
cd ~
git clone https://codeberg.org/comaps/subways.git
copy-coasts:
if: inputs.jobs == 'copy-coasts' || inputs.jobs == 'all-except-upload'
name: Copy Previously Generated Coasts
runs-on: mapfilemaker
needs:
- clone-repos
container:
image: codeberg.org/comaps/maps_generator:f6d53d54f794
volumes:
- /mnt/4tbexternal/:/mnt/4tbexternal/
- /mnt/4tbexternal/osm-planet:/home/planet
concurrency:
group: ${{ github.workflow }}-map-generator-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
steps:
- name: Copy Coasts
shell: bash
run: |
echo "WorldCoasts available:"
ls -al /mnt/4tbexternal/osm-maps/*/intermediate_data/WorldCoasts.*
if [ -f /mnt/4tbexternal/osm-maps/*/intermediate_data/WorldCoasts.geom ]; then
echo "Before:"
ls -al /home/planet/latest_coasts*
cp -p /mnt/4tbexternal/osm-maps/*/intermediate_data/WorldCoasts.geom /home/planet/latest_coasts.geom
cp -p /mnt/4tbexternal/osm-maps/*/intermediate_data/WorldCoasts.rawgeom /home/planet/latest_coasts.rawgeom
echo "After:"
ls -al /home/planet/latest_coasts*
else
echo "No WorldCoasts found."
fi
update-planet:
if: inputs.jobs == 'planet' || inputs.jobs == 'all-except-upload'
name: Update Planet
runs-on: mapfilemaker
needs:
- clone-repos
container:
image: codeberg.org/comaps/maps_generator:f6d53d54f794
volumes:
- /mnt/4tbexternal/:/mnt/4tbexternal/
- /mnt/4tbexternal/osm-planet:/home/planet
concurrency:
group: ${{ github.workflow }}-map-generator-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
steps:
- name: Download Planet File if Absent
shell: bash
# TODO: replace wget2 with curl -Z
run: |
if [ ! -d /home/planet/planet/ ]; then
mkdir -p /home/planet/planet/
fi
if [ ! -f /home/planet/planet/planet-latest.osm.pbf ]; then
cd /home/planet/planet/
wget2 --verbose --progress=bar --continue https://ftpmirror.your.org/pub/openstreetmap/pbf/planet-latest.osm.pbf
else
echo "planet-latest.osm.pbf was found, raw download not required."
fi
- name: Update Planet
shell: bash
run: |
cd /home/planet/planet/
rm -f planet-latest-new.osm.pbf
pyosmium-up-to-date planet-latest.osm.pbf -o planet-latest-new.osm.pbf -v --size 16384
mv planet-latest-new.osm.pbf planet-latest.osm.pbf
- name: Converting planet-latest.osm.pbf to planet.o5m
# TODO: better to run osmupdate (not convert) just before starting the maps jobs - for max fresh data.
run: |
echo "Starting..."
cd /home/planet/planet/
osmconvert -v --drop-author --drop-version --hash-memory=4000 planet-latest.osm.pbf -o=planet.o5m
echo "Done."
- name: Notify Zulip
run: |
curl -X POST https://comaps.zulipchat.com/api/v1/messages \
-u $ZULIP_BOT_EMAIL:$ZULIP_API_KEY \
--data-urlencode type=stream \
--data-urlencode 'to="DevOps"' \
--data-urlencode topic=codeberg-bot \
--data-urlencode 'content=Planet update is done!'
wiki-update:
if: inputs.jobs == 'wiki' || inputs.jobs == 'all-except-upload'
name: Update Wikipedia
runs-on: mapfilemaker
needs:
- clone-repos
container:
image: codeberg.org/comaps/maps_generator:f6d53d54f794
volumes:
- /mnt/4tbexternal/:/mnt/4tbexternal/
- /mnt/4tbexternal/osm-planet:/home/planet
concurrency:
group: ${{ github.workflow }}-map-generator-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
steps:
- uses: actions/cache@v4
with:
path: "~"
key: cache-${{ github.run_id }}-${{ github.run_attempt }}
- name: Check for planet file
shell: bash
# TODO: remove debug output
run: |
if [ ! -f /home/planet/planet/planet-latest.osm.pbf ]; then
echo "ERROR: No file at /home/planet/planet/planet-latest.osm.pbf"
ls -al /home/planet/
ls -al /home/planet/planet/
exit 1
fi
- name: Only get new dumps once per 30 days
shell: bash
run: |
if [[ '${{ inputs.reset }}' == 'wiki-ratelimit' ]]; then
echo "Bypassing wiki rate limit upon request."
exit 0
fi
datediff() {
d1=$(date -d "$1" +%s)
d2=$(date -d "$2" +%s)
echo $(( (d1 - d2) / 86400 ))
}
RECENTDUMPDATE=$(find /home/planet/wikipedia/dumps/ -mindepth 1 -maxdepth 1 -iname "2*" -type d | sort -n -r | head -1 | cut -d/ -f6)
TODAY=$(date +%Y%m%d)
DATEDIFF=$(datediff $TODAY $RECENTDUMPDATE)
if [ $DATEDIFF -lt 30 ]; then
echo "ERROR: The most recent wiki dump is from $RECENTDUMPDATE, $DATEDIFF days ago. Wikimedia limits users to 15 snapshot requests per month."
echo "Set the 'reset' option to 'wiki-ratelimit' to bypass this."
ls -al /home/planet/wikipedia/dumps/
exit 1
fi
- name: Update Wikipedia from Enterprise API
shell: bash
run: |
#todo: curl in download.sh can fail when rate limited and even save error messages to the output. need to validate.
#downloading all languages can also trigger rate limits or fail as well. needs work.
#also: a failure to download means a failure to build, and could result in no wiki descriptions etc.
#also-also: do we want to remove old wiki data in planet between builds? pastk: no need, its being updated / augmented
mkdir -p /home/planet/wikipedia/dumps
mkdir -p /home/planet/wikipedia/build
cd ~/wikiparser
ls -al
echo "Downloading ..."
./download.sh /home/planet/wikipedia/dumps
ls -al /home/planet/wikipedia/dumps/*
echo "Running ..."
./run.sh /home/planet/wikipedia/build \
/home/planet/planet/planet-latest.osm.pbf \
/home/planet/wikipedia/dumps/latest/*.tar.gz
echo "DONE"
- name: Check that the latest dumps are present, recent, and not super tiny
shell: bash
run: |
FAILCHECK=0
# Check all .tar.gz files in /home/planet/wikipedia/dumps/latest/
for file in /home/planet/wikipedia/dumps/latest/*.tar.gz; do
# Check if file exists (handles case where glob doesn't match)
[ -e "$file" ] || continue
# Get file size in MB and modification time in days
size_mb=$(stat -f%z "$file" 2>/dev/null | awk '{print int($1/1024/1024)}' || stat -c%s "$file" | awk
'{print int($1/1024/1024)}')
days_old=$(find "$file" -mtime -7 | wc -l)
# Verify conditions
if [ "$size_mb" -lt 100 ]; then
echo "FAIL: $file is only ${size_mb}MB (< 100MB)"
FAILCHECK=1
elif [ "$days_old" -eq 0 ]; then
echo "FAIL: $file is older than 7 days"
ls -al $file
FAILCHECK=1
else
echo "PASS: $file (${size_mb}MB, modified within 7 days)"
fi
done
exit $FAILCHECK
- name: Notify Zulip
run: |
curl -X POST https://comaps.zulipchat.com/api/v1/messages \
-u $ZULIP_BOT_EMAIL:$ZULIP_API_KEY \
--data-urlencode type=stream \
--data-urlencode 'to="DevOps"' \
--data-urlencode topic=codeberg-bot \
--data-urlencode 'content=Wiki update is done!'
update-isolines:
if: inputs.jobs == 'isolines' || inputs.jobs == 'all-except-upload'
name: Update Isolines
runs-on: mapfilemaker
needs:
- clone-repos
container:
image: codeberg.org/comaps/maps_generator:f6d53d54f794
volumes:
- /mnt/4tbexternal/:/mnt/4tbexternal/
- /mnt/4tbexternal/osm-planet:/home/planet
concurrency:
group: ${{ github.workflow }}-map-generator-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
steps:
- uses: actions/cache@v4
with:
path: "~"
key: cache-${{ github.run_id }}-${{ github.run_attempt }}
# TODO: we only need to update these if our SRTM or countries change
# TODO: after update, verify that sizable files exist: /home/planet/isolines/*.isolines
- name: Update Isolines
shell: bash
# TODO: preserve previous isolines version?
# TODO: cleanup the tmp-tiles dir after completion
run: |
cd ~/comaps/
./tools/unix/build_omim.sh -p ~ -R topography_generator_tool
rm -rf /home/planet/isolines/
mkdir /home/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=/home/planet/isolines/tmp-tiles/ \
--countries_isolines_out_dir=/home/planet/isolines/ \
--data_dir=./data/ \
--srtm_path=/home/planet/SRTM-patched-europe/ \
--threads=96
- name: Check isolines
shell: bash
run: |
NUMISO=$(ls -al /home/planet/isolines/*.isolines | wc -l)
echo "Found $NUMISO isolines"
if [ $NUMISO -lt 10 ]; then
echo "ERROR: Did generation fail?"
exit 1
fi
- name: Notify Zulip
run: |
curl -X POST https://comaps.zulipchat.com/api/v1/messages \
-u $ZULIP_BOT_EMAIL:$ZULIP_API_KEY \
--data-urlencode type=stream \
--data-urlencode 'to="DevOps"' \
--data-urlencode topic=codeberg-bot \
--data-urlencode 'content=Isolines are done!'
update-subways:
if: inputs.jobs == 'subways' || inputs.jobs == 'all-except-upload'
name: Update Subways
runs-on: mapfilemaker
needs:
- clone-repos
container:
image: codeberg.org/comaps/maps_generator:f6d53d54f794
volumes:
- /mnt/4tbexternal/:/mnt/4tbexternal/
- /mnt/4tbexternal/osm-planet:/home/planet
concurrency:
group: ${{ github.workflow }}-map-generator-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
steps:
- uses: actions/cache@v4
with:
path: "~"
key: cache-${{ github.run_id }}-${{ github.run_attempt }}
- name: Update Subways
shell: bash
run: |
cd ~/comaps/
cp tools/unix/maps/settings.sh.prod tools/unix/maps/settings.sh
./tools/unix/maps/generate_subways.sh
- name: Notify Zulip
run: |
curl -X POST https://comaps.zulipchat.com/api/v1/messages \
-u $ZULIP_BOT_EMAIL:$ZULIP_API_KEY \
--data-urlencode type=stream \
--data-urlencode 'to="DevOps"' \
--data-urlencode topic=codeberg-bot \
--data-urlencode 'content=Subways are done!'
update-tiger:
if: inputs.jobs == 'tiger' || inputs.jobs == 'all-except-upload'
name: Update TIGER
runs-on: mapfilemaker
needs:
- clone-repos
container:
image: codeberg.org/comaps/maps_generator:f6d53d54f794
volumes:
- /mnt/4tbexternal/:/mnt/4tbexternal/
- /mnt/4tbexternal/osm-planet:/home/planet
concurrency:
group: ${{ github.workflow }}-map-generator-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
steps:
- uses: actions/cache@v4
with:
path: "~"
key: cache-${{ github.run_id }}-${{ github.run_attempt }}
- name: Build address_parser
shell: bash
run: |
cd ~/comaps
#rm -rf ~/omim-build-relwithdebinfo/CMakeCache.txt
#rm -rf ~/omim-build-relwithdebinfo/CMakeFiles
./tools/unix/build_omim.sh -p ~ -R address_parser_tool
- name: Update TIGER from Nominatim
shell: bash
# TODO: use curl instead of wget2
run: |
# TODO: maybe remove old osm-planet/tiger first?
cd /home/planet/
mkdir -p tiger
wget2 https://nominatim.org/data/tiger-nominatim-preprocessed-latest.csv.tar.gz
cd ~/comaps
tar -xOzf /home/planet/tiger-nominatim-preprocessed-latest.csv.tar.gz | ~/omim-build-relwithdebinfo/address_parser_tool --output_path=/home/planet/tiger
generate-maps:
if: inputs.jobs == 'maps' || inputs.jobs == 'all-except-upload'
name: Generate Maps
runs-on: mapfilemaker
needs:
- clone-repos
timeout-minutes: 40320
container:
image: codeberg.org/comaps/maps_generator:f6d53d54f794
volumes:
- /mnt/4tbexternal/:/mnt/4tbexternal/
- /mnt/4tbexternal/osm-planet:/home/planet
options: --ulimit nofile=262144:262144
concurrency:
group: ${{ github.workflow }}-map-generator-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
steps:
- uses: actions/cache@v4
with:
path: "~"
key: cache-${{ github.run_id }}-${{ github.run_attempt }}
- name: Make output folders if necessary
shell: bash
run: |
if [ ! -d /mnt/4tbexternal/osm-maps ]; then
mkdir -p /mnt/4tbexternal/osm-maps
fi
- name: Get SRTM if necessary
# TODO: it should be a separate step like Wiki or isolines
shell: bash
run: |
if [ ! -d /home/planet/SRTM-patched-europe/ ]; then
echo "ERROR: NO SRTM"
exit 1
fi
- name: Run docker_maps_generator.sh
shell: bash
run: |
cd ~/comaps
bash ./tools/unix/maps/docker_maps_generator.sh
- name: Notify Zulip
run: |
curl -X POST https://comaps.zulipchat.com/api/v1/messages \
-u $ZULIP_BOT_EMAIL:$ZULIP_API_KEY \
--data-urlencode type=stream \
--data-urlencode 'to="DevOps"' \
--data-urlencode topic=codeberg-bot \
--data-urlencode 'content=Generator is done!'
upload-maps:
if: inputs.jobs == 'upload'
name: Upload Maps
runs-on: mapfilemaker
container:
image: codeberg.org/comaps/maps_generator:f6d53d54f794
volumes:
- /mnt/4tbexternal/:/mnt/4tbexternal/
- /mnt/4tbexternal/osm-planet:/home/planet
concurrency:
group: ${{ github.workflow }}-map-generator-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
steps:
- name: Write config file
run: |
mkdir -p ~/.config/rclone/
echo "${{ secrets.RCLONE_CONF }}" > ~/.config/rclone/rclone.conf
- name: Upload map files to CDNs
shell: bash
run: |
shopt -s nullglob
buildfolder=$(find /mnt/4tbexternal/osm-maps/ -mindepth 1 -maxdepth 1 -iname "2*" -type d | sort -n -r | head -1 | cut -d/ -f5)
builddate=$(find /mnt/4tbexternal/osm-maps/*/ -mindepth 1 -maxdepth 1 -iname "2*" -type d | sort -n -r | head -1 | cut -d/ -f6)
mwmfiles=( /mnt/4tbexternal/osm-maps/$buildfolder/$builddate/*.mwm )
if (( ${#mwmfiles[@]} )); then
echo "<$(date +%T)> Uploading maps from $buildfolder/$builddate..."
cd ~/comaps/tools/unix/maps
./upload_to_cdn.sh /mnt/4tbexternal/osm-maps/$buildfolder/$builddate
echo "<$(date +%T)> Finished uploading maps from $buildfolder/$builddate."
else
echo "<$(date +%T)> No MWM files in /mnt/4tbexternal/osm-maps/$buildfolder/$builddate/*.mwm, not uploading maps."
echo "<$(date +%T)> Found top level: $(ls -alt /mnt/4tbexternal/osm-maps/*)"
echo "<$(date +%T)> Found second level: $(ls -alt /mnt/4tbexternal/osm-maps/$buildfolder/*)"
fi
- name: Notify Zulip
run: |
curl -X POST https://comaps.zulipchat.com/api/v1/messages \
-u $ZULIP_BOT_EMAIL:$ZULIP_API_KEY \
--data-urlencode type=stream \
--data-urlencode 'to="DevOps"' \
--data-urlencode topic=codeberg-bot \
--data-urlencode 'content=Upload is done!'

2
.gitignore vendored
View File

@@ -9,7 +9,6 @@ Makefile.Release
object_script.*.Debug
object_script.*.Release
compile_commands.json
*.local.*
stxxl.errlog
stxxl.log
@@ -21,6 +20,7 @@ data/symbols/**/symbols.sdf
data/bookmarks
data/edits.xml
data/traffic.xml
data/World.mwm
data/WorldCoasts.mwm
data/world_mwm/*

View File

@@ -175,10 +175,10 @@ if (NOT PLATFORM_IPHONE AND NOT PLATFORM_ANDROID)
find_package(Qt6 COMPONENTS REQUIRED ${qt_components} PATHS $ENV{QT_PATH} /opt/homebrew/opt/qt@6 /usr/local/opt/qt@6 /usr/lib/x86_64-linux-gnu/qt6)
set(MINIMUM_REQUIRED_QT_VERSION 6.4.0)
if (Qt6_VERSION VERSION_LESS ${MINIMUM_REQUIRED_QT_VERSION})
message(FATAL_ERROR "Unsupported Qt version: ${Qt6_VERSION}, the minimum required is ${MINIMUM_REQUIRED_QT_VERSION}")
if (Qt6Widgets_VERSION VERSION_LESS ${MINIMUM_REQUIRED_QT_VERSION})
message(FATAL_ERROR "Unsupported Qt version: ${Qt6Widgets_VERSION}, the minimum required is ${MINIMUM_REQUIRED_QT_VERSION}")
else()
message(STATUS "Found Qt version: ${Qt6_VERSION}")
message(STATUS "Found Qt version: ${Qt6Widgets_VERSION}")
endif()
endif()
@@ -222,11 +222,6 @@ if (PLATFORM_DESKTOP AND NOT WITH_SYSTEM_PROVIDED_3PARTY)
include_directories("${PROJECT_BINARY_DIR}/3party/gflags/include")
endif()
# Android fails to find boost in many cases, this fixes it.
if (PLATFORM_ANDROID)
include_directories("${OMIM_ROOT}/3party/boost")
endif()
# Used in qt/ and shaders/
find_package(Python3 REQUIRED COMPONENTS Interpreter)

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" 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&color=588157" 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" 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&color=588157" 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

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,33 +0,0 @@
En fællesskabdrevet og åben source kortapp, baseret på kortdata fra OpenStreetMap og styrket i forpligtelsen til værdierne gennemsigtighed, privatlivets fred, og non-profit. CoMaps udspringer af Organic Maps, som selv udsprang af Maps.ME.
Læs mere om grundlaget for projektet og dets udviklingsretnign på <b><i>codeberg.org/comaps</i></b>.
Slut dig til fælleskabet og hjælp til med at bygge den bedste kortapp i verden.
• Brug appen og fortæl andre om den
• Giv feedback anmeld fejl
• Opdater kortdata i appen eller på OpenStreetMap-hjemmesiden.
‣ <b>Offlinefokuseret</b>: Planlæg din rute og find vej i udlandet uden brug af mobildata, søg og find afsidesliggende mål på en afsidesliggende vandretur, mm. Alle funktioner er designet til at fungere uden internetforbindelse.
‣ <b>Respekt for privatlivets fred</b>: Appen er designet med henblik på at respektere dit privatliv den identificerer dig ikke, indeholder ingen sporingsmekanismer, og insamler ingen personlig information. Appen er reklamefri.
‣ <b>Enkel og elegant</b>: de essentielle funktioner er nemme at bruge, og de virker bare.
‣ <b>Sparer på batteriet og på lagerpladsen</b>: Dræner ikke dit batteri hurtigt, som andre kortapps. De kompakte kortfiler minimerer varigt lagerpladsforbrug.
‣ <b>Gratis og bygget i fællesskab</b>: Folk som dig har hjulpet med denne app ved at tilføje steder til OpenStreetMap, ved at teste appens funktioner og give feedback på dem og ved at bidrage til udviklingen af appen med deres tid og penge.
‣ <b>Åben og gennemsigtig beslutningstagningsproces og finanser, non-profit, og fuldt ud åben source.</b>
<b>Hovedfunktioner</b>
• Hent detaljerede kort, der indeholder steder som ikke findes i mange kommericelle kort.
• En frilufts-tilstand med markede vandrestier, teltpladser, kilder, bjerg- og bakketoppe, højdekonturlinjer, mm.
• Gangstier og cykelstier
• Steder, der kan besøges, som f.eks. restauranter, tankstationer, hoteller, butikker, seværdigheder og mange andre.
• Søg efter stednavn, adresse, eller type af sted.
• Gem dine yndlingssteder som bogmærker med et enkelt tryk.
• iCloud synkronisering af bogmærker og optagede spor.
• Offline artikler fra Wikipedia.
• Metro-lag med navigation.
• Optagelse af spor.
• Eksport og import af bogmærker og spor i formaterne KML, KMZ og GPX.
• Mørk tilstand til brug om natten.
• Mulighed for at forbedre kortet vha. en indbygget editor.
• CarPlay understøttes.
<b>Friheden er ankommet</b>
Opdag din rejse, find vej i verden med privatliv og fællesskab i førersædet!

View File

@@ -0,0 +1,9 @@
• Verbesserte Sichtbarkeit & Benutzeroberfläche für Navigationsanweisungen
• Option um Treppen zu vermeiden
• Verbesserte Suche in mehreren Sprachen
• Spezifisches Symbol für Busbahnöfe hinzugefügt
• Probleme mit Android Auto behoben (via OM)
• Verbesserter Editor mit kleinere Bugfixes
• Kartenstile verbessert (via OM)
• Verbesserte Übersetzungen
Für weitere Änderungen siehe Codeberg-Versionshinweise

Binary file not shown.

Before

Width:  |  Height:  |  Size: 636 KiB

After

Width:  |  Height:  |  Size: 628 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 407 KiB

After

Width:  |  Height:  |  Size: 532 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 155 KiB

After

Width:  |  Height:  |  Size: 391 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 454 KiB

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 451 KiB

After

Width:  |  Height:  |  Size: 268 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 357 KiB

After

Width:  |  Height:  |  Size: 263 KiB

View File

@@ -1,13 +1,9 @@
Fixed voice directions pronouncing weird symbols in the beginning
OpenStreetMap data as of November 23
Changes in the previous release:
Added trees
Made bus stop icons smaller and show up earlier
Reduce visibility of entrances
Added several other POI types
• Show sand areas on the map
• Add business is vacant option to the OSM editor
• Improved road shields in Europe
• Avoid paved roads routing option
• Added icons to the settings page
Improved visibility and UI of instructions in navigation
Added option to avoid steps
• Improved search in multiple languages
• Added specific icon for bus stations
Fixed Android Auto issues (via OM project)
Improved editor and fix minor issues
Improved map styles (via OM project)
Improved app translations
Check our Codeberg release notes for more changes!

View File

@@ -0,0 +1,8 @@
• Mejora de la visibilidad y la interfaz de usuario de las instrucciones de navegación
• Se ha añadido la opción de evitar escaleras
• Mejora de la búsqueda en varios idiomas como ES
• Se ha añadido un icono específico para las estaciones de autobús
• Se han solucionado los problemas de Android Auto (a través del proyecto OM)
• Se ha mejorado el editor y se han solucionado pequeños problemas
• Se han mejorado los estilos de los mapas (a través del proyecto OM)
Más detalles en Codeberg

Binary file not shown.

Before

Width:  |  Height:  |  Size: 605 KiB

After

Width:  |  Height:  |  Size: 655 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 407 KiB

After

Width:  |  Height:  |  Size: 532 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 164 KiB

After

Width:  |  Height:  |  Size: 391 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 452 KiB

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 460 KiB

After

Width:  |  Height:  |  Size: 254 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 355 KiB

After

Width:  |  Height:  |  Size: 263 KiB

View File

@@ -0,0 +1,9 @@
• Interface utilisateur et visibilité des instructions en navigation améliorée
• Option pour éviter les escaliers ajoutée
• Recherche améliorée dans différents languages
• Icône pour les gares routières ajoutée
• Corrections de bugs liées à Android Auto (via OM)
• Editeur amélioré et corrections de bugs
• Style de la carte amélioré (via OM)
• Traductions améliorées
Plus d'informations sur notre Codeberg

View File

@@ -0,0 +1,9 @@
• Visibilidade e interface do usuário aprimoradas para instruções na navegação
• Opção adicionada para evitar degraus
• Busca aprimorada em vários idiomas
• Adição de ícone específico para rodoviárias
• Problemas corrigidos no Android Auto (via projeto OM)
• Editor aprimorado e correção de problemas menores
• Estilos de mapa aprimorados (via projeto OM)
• Traduções aprimoradas
Confira nossas notas de lançamento do Codeberg para mais mudanças!

View File

@@ -1,32 +0,0 @@
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

@@ -0,0 +1,9 @@
• Лучшая видимость и интерфейс при навигации
• Добавлена возможность пропускать шаги
• Улучшен поиск на нескольких языках
• Новый значок автостанций
• Исправлены проблемы с Android Auto (через OM)
• Улучшен редактор и исправлены мелкие недочёты
• Улучшены стили карт (через OM)
• Улучшены переводы приложения
Ознакомьтесь с примечаниями к выпуску Codeberg, чтобы узнать изменениях!

View File

@@ -1,31 +0,0 @@
Brezplačno in odprtokodno zemljevidno orodje, ki ga vodi skupnost, temelji na podatkih OpenStreetMap in je okrepljena s predanostjo transparentnosti, zasebnosti in nedobičkonosnosti. CoMaps je izpeljanka OrganicMaps, ta pa je izpeljanka Maps.ME.
Preverite si o razlogih za ta projekt in njegovi usmerjenosti na <b><i>codeberg.org/comaps</i></b>.
Pridružite se skupnosti in pomagajte narediti najboljše zemljevidno orodje
• Uporabljajte orodje in širite glas o njem
• Dajajte povratne informacije in poročajte o napakah
• Posodabljajte podatke zemljevida v tem orodju ali na spletni strani OpenStreetMap
‣ <b>Osredotočeno na uporabo brez povezave</b>: Načrtujte in se usmerjajte na vašem potovanju v tujini vrez potrebe po mobilnih podatkih, iščite vmesne točke potocanja ko ste na daljšem pohodu ipd. Vse zmogljivosti orodja so zasnovane za delo brez povezave.
‣ <b>Spoštovanje zasebnosti</b>: orodje je zasnovano z mislijo na zasebnost ne prepoznava oseb, ne sledi in ne zbira osebnih podatkov. Brez oglasov.
‣ <b>Preprosto in dodelano</b>: nujne zmogljivosti, enostavne za uporabo, ki preprosto delujejo.
‣ <b>Prihrani vašo baterijo in prostor.</b>: ne izčrpava vaše baterije kakor druga usmerjevalna orodja. Strnjeni zemljevidi prihranijo dragocen prostor na vašem telefonu.
‣ <b>Brezplačno in ustvarjeno v skupnosti</b>: ljudje kot ste vi pomagajo ustvarjati to orodje, tako da dodajajo kraje na OpenStreetMap, preizkušajo in dajejo povratne informacije o zmogljivostih in prispevajo svoje razvijalske sposobnosti in sredstva.
‣ <b>Odprto in transparentno odločanje in finance, nedobičkonosno in popolnoma odprtokodno.</b>
<b>Glavne zmogljivosti</b>:
• Prenosljivi podrobni zemljevidi s kraji, ki na Googlovoh zemljevidih niso na voljo.
• Prikaz za dejavnosti na prostem s poudarjenimi pohodniškimi potmi, tabornimi prostori, vodnimi viri, vrhovi, plastnicami itd.
• Pešpoti in kolesarke poti
• Kraji zanimanja, npr. restavracije, bencinske črpalke, hoteli, trgovine, znamenitosti in mnogo več
• Iščite po imenu, hišnemu naslovu ali po vrsti
• Usmerjanje z glasovnimi obvestili za hojo, kolesarjenje ali vožnjo avtomobila.
• Zaznamujte svoje najljubše kraje s preprostim dotikom
• Wikipedijini članki brez povezave
• Prometna plast podzemne železnice z usmerjanjem
• Izvozite ali uvozite zaznamke in sledi v oblikah KML, KMZ, GPX
• Temni prikaz za uporabo ponoči
• Izboljšajtw podatke zemljevida za vse z uporabo vgrajenega urejevalnika
<b>Svoboda je tu</b>
Odkijte več o vašem potovanju, usmerjajte se po svetu s poudarkom na zasebnosti in skupnostnem delovanju!

View File

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

View File

@@ -1 +0,0 @@
Comaps- Vandra, Cykla, Kör Offline, Privat

View File

@@ -1,32 +0,0 @@
OpenStreetMap தரவை அடிப்படையாகக் கொண்ட சமூகம் தலைமையிலான இலவச மற்றும் திறந்த மூல வரைபட பயன்பாடு மற்றும் வெளிப்படைத்தன்மை, தனியுரிமை மற்றும் இலாப நோக்கற்றது ஆகியவற்றுக்கான அர்ப்பணிப்புடன் வலுவூட்டப்பட்டது. CoMaps என்பது ஆர்கானிக் மேப்சின் ஃபோர்க்/ச்பின்-ஆஃப் ஆகும், இது Maps.ME இன் ஃபோர்க் ஆகும்.
திட்டத்திற்கான காரணங்கள் மற்றும் அதன் திசையை <b><i>codeberg.org/comaps</i></b> இல் படிக்கவும்.
அங்குள்ள சமூகத்தில் சேர்ந்து சிறந்த வரைபட பயன்பாட்டை உருவாக்க உதவுங்கள்
• பயன்பாட்டைப் பயன்படுத்தி, அதைப் பற்றிய தகவலைப் பரப்புங்கள்
• கருத்துக்களை வழங்கவும் மற்றும் சிக்கல்களைப் புகாரளிக்கவும்
• பயன்பாட்டில் அல்லது OpenStreetMap இணையதளத்தில் வரைபடத் தரவைப் புதுப்பிக்கவும்
‣ <b>ஆஃப்லைனில் கவனம் செலுத்தப்பட்டது</b>: செல்லுலார் சேவையின் தேவையின்றி உங்களின் வெளிநாட்டுப் பயணத்தைத் திட்டமிட்டு வழிநடத்துங்கள், தொலைதூர பயணத்தில் இருக்கும் போது வழிப் புள்ளிகளைத் தேடுங்கள்.
‣ <b>தனியுரிமைக்கு மதிப்பளித்தல்</b>: பயன்பாடு தனியுரிமையை மனதில் கொண்டு வடிவமைக்கப்பட்டுள்ளது - நபர்களை அடையாளம் காணாது, கண்காணிக்காது மற்றும் தனிப்பட்ட தகவல்களைச் சேகரிக்காது. விளம்பரங்கள் இல்லாதது.
‣ <b>எளிமையான மற்றும் மெருகூட்டப்பட்டது</b>: செயல்படும் நற்பொருத்தங்கள் பயன்படுத்த எளிதானது.
‣ <b>உங்கள் பேட்டரி மற்றும் இடத்தைச் சேமிக்கிறது</b>: மற்ற வழிசெலுத்தல் பயன்பாடுகளைப் போல உங்கள் பேட்டரியை வெளியேற்றாது. சிறிய வரைபடங்கள் உங்கள் தொலைபேசியில் விலைமதிப்பற்ற இடத்தை சேமிக்கின்றன.
‣ <b>இலவசம் மற்றும் சமூகத்தால் உருவாக்கப்பட்டது</b>: OpenStreetMap இல் இடங்களைச் சேர்ப்பதன் மூலமும், சோதனை செய்து, அம்சங்களைப் பற்றிய கருத்துக்களை வழங்குவதன் மூலமும், அவர்களின் மேம்பாட்டுத் திறன்களையும் பணத்தையும் பங்களிப்பதன் மூலமும் உங்களைப் போன்றவர்கள் பயன்பாட்டை உருவாக்க உதவியுள்ளனர்.
‣ <b>திறந்த மற்றும் வெளிப்படையான முடிவெடுக்கும் மற்றும் நிதியியல், இலாப நோக்கற்ற மற்றும் முழு திறந்த மூல.</b>
<b>முக்கிய அம்சங்கள்</b>:
• கூகுள் மேப்சில் இல்லாத இடங்களுடன் தரவிறக்கம் செய்யக்கூடிய விரிவான வரைபடங்கள்
• ஐகிங் பாதைகள், முகாம்கள், நீர் ஆதாரங்கள், சிகரங்கள், விளிம்பு கோடுகள் போன்றவற்றைக் கொண்ட வெளிப்புறப் பயன்முறை
• நடைபாதைகள் மற்றும் சைக்கிள் பாதைகள்
• உணவகங்கள், எரிவாயு நிலையங்கள், ஓட்டல்கள், கடைகள், சுற்றிப்பார்க்கும் இடங்கள் மற்றும் பல போன்ற ஆர்வமுள்ள இடங்கள்
• பெயர் அல்லது முகவரி அல்லது ஆர்வமுள்ள வகை மூலம் தேடவும்
• நடைபயிற்சி, சைக்கிள் ஓட்டுதல் அல்லது வண்டி ஓட்டுவதற்கான குரல் அறிவிப்புகளுடன் வழிசெலுத்தல்
• ஒரே தட்டினால் உங்களுக்குப் பிடித்த இடங்களை புத்தகக்குறி செய்யவும்
• இணைப்பில்லாத விக்கிபீடியா கட்டுரைகள்
• சுரங்கப்பாதை போக்குவரத்து அடுக்கு மற்றும் திசைகள்
• ட்ராக் ரெக்கார்டிங்
• KML, KMZ, GPX வடிவங்களில் புக்மார்க்குகள் மற்றும் டிராக்குகளை ஏற்றுமதி மற்றும் இறக்குமதி செய்யுங்கள்
• இரவில் பயன்படுத்த ஒரு இருண்ட பயன்முறை
• அடிப்படை உள்ளமைக்கப்பட்ட எடிட்டரைப் பயன்படுத்தி அனைவருக்கும் வரைபடத் தரவை மேம்படுத்தவும்
<b>சுதந்திரம் இங்கே உள்ளது</b>
உங்கள் பயணத்தைக் கண்டறியவும், தனியுரிமை மற்றும் சமூகத்தை முன்னணியில் கொண்டு உலகிற்கு செல்லவும்!

View File

@@ -1 +0,0 @@
எளிய வழிகாட்டி - பயணத்தை மேலும் சுவாரசியமாக்க - சமூகத்தால் இயக்கப்படுகிறது

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.11.25-4-FDroid+25112504
version: 2025.07.23-4-FDroid+25072304

View File

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

View File

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

View File

@@ -1,36 +0,0 @@
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

@@ -1,36 +0,0 @@
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 @@
Enostavno usmerjanje Odkrij več o svojem potovanju Podprto v skupnosti
Enostavna navigacija Odkrij več o svojem potovanju Podprto v skupnosti

View File

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

View File

@@ -1 +0,0 @@
Comaps- Navigera Privat

View File

@@ -1 +0,0 @@
எளிய வழிகாட்டி - பயணத்தை மேலும் சுவாரசியமாக்க - சமூகத்தால் இயக்கப்படுகிறது

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

@@ -62,6 +62,21 @@
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https" />
</intent>
<intent>
<action android:name="org.traffxml.traff.GET_CAPABILITIES"/>
</intent>
<intent>
<action android:name="org.traffxml.traff.POLL"/>
</intent>
<intent>
<action android:name="org.traffxml.traff.SUBSCRIBE"/>
</intent>
<intent>
<action android:name="org.traffxml.traff.SUBSCRIPTION_CHANGE"/>
</intent>
<intent>
<action android:name="org.traffxml.traff.UNSUBSCRIBE"/>
</intent>
</queries>
<supports-screens

View File

@@ -1,13 +1,11 @@
package app.organicmaps.background;
import android.content.Context;
import android.os.Build;
import androidx.annotation.NonNull;
import androidx.work.Constraints;
import androidx.work.ExistingWorkPolicy;
import androidx.work.NetworkType;
import androidx.work.OneTimeWorkRequest;
import androidx.work.OutOfQuotaPolicy;
import androidx.work.WorkManager;
import androidx.work.Worker;
import androidx.work.WorkerParameters;
@@ -37,11 +35,7 @@ public class OsmUploadWork extends Worker
if (Editor.nativeHasSomethingToUpload() && OsmOAuth.isAuthorized())
{
final Constraints c = new Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build();
OneTimeWorkRequest.Builder builder = new OneTimeWorkRequest.Builder(OsmUploadWork.class).setConstraints(c);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
builder.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST);
}
final OneTimeWorkRequest wr = builder.build();
final OneTimeWorkRequest wr = new OneTimeWorkRequest.Builder(OsmUploadWork.class).setConstraints(c).build();
WorkManager.getInstance(context).beginUniqueWork("UploadOsmChanges", ExistingWorkPolicy.KEEP, wr).enqueue();
}
}

View File

@@ -7,9 +7,15 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StyleRes;
import androidx.fragment.app.DialogFragment;
import app.organicmaps.R;
public class BaseMwmDialogFragment extends DialogFragment
{
@StyleRes
protected final int getFullscreenTheme()
{
return R.style.MwmTheme_DialogFragment_Fullscreen;
}
protected int getStyle()
{

View File

@@ -18,7 +18,9 @@ import androidx.fragment.app.FragmentManager;
import app.organicmaps.MwmApplication;
import app.organicmaps.R;
import app.organicmaps.SplashActivity;
import app.organicmaps.sdk.util.Config;
import app.organicmaps.sdk.util.log.Logger;
import app.organicmaps.util.RtlUtils;
import com.google.android.material.appbar.MaterialToolbar;
import java.util.Objects;
@@ -40,6 +42,7 @@ public abstract class BaseMwmFragmentActivity extends AppCompatActivity
{
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this, SystemBarStyle.dark(Color.TRANSPARENT));
RtlUtils.manageRtl(this);
if (!MwmApplication.from(this).getOrganicMaps().arePlatformAndCoreInitialized())
{
final Intent intent = Objects.requireNonNull(getIntent());

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_edit,
items.add(new MenuBottomSheetItem(R.string.edit, R.drawable.ic_settings,
() -> 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

@@ -466,10 +466,12 @@ 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, R.drawable.ic_search_fail);
requirePlaceholder().setContent(R.string.search_not_found, R.string.search_not_found_query);
}
else if (isEmpty())
{
requirePlaceholder().setContent(R.string.bookmarks_empty_list_title, R.string.bookmarks_empty_list_message, R.drawable.ic_bookmarks);
requirePlaceholder().setContent(R.string.bookmarks_empty_list_title, R.string.bookmarks_empty_list_message);
}
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_edit, this::onSettingsOptionSelected));
items.add(new MenuBottomSheetItem(R.string.edit, R.drawable.ic_settings, this::onSettingsOptionSelected));
if (!isLastOwnedCategory())
items.add(new MenuBottomSheetItem(R.string.delete_list, R.drawable.ic_delete, this::onDeleteOptionSelected));
return items;

View File

@@ -438,17 +438,21 @@ 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());
}
@@ -458,12 +462,9 @@ public class Holders
String formattedDesc = desc.replace("\n", "<br>");
Spanned spannedDesc = Utils.fromHtml(formattedDesc);
if (!TextUtils.isEmpty(spannedDesc)) {
mDescText.setText(spannedDesc);
}
else {
mDescText.setText(R.string.list_description_empty);
}
mDescText.setText(spannedDesc);
UiUtils.showIf(!TextUtils.isEmpty(spannedDesc), mDescText);
}
}
}

View File

@@ -32,8 +32,7 @@ public class DrivingOptionsScreen extends BaseMapScreen
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.Steps, R.string.avoid_steps),
new DrivingOption(RoadType.Paved, R.string.avoid_paved)};
new DrivingOption(RoadType.Steps, R.string.avoid_steps)};
@NonNull
private final Map<RoadType, Boolean> mInitialDrivingOptionsState = new HashMap<>();

View File

@@ -357,7 +357,7 @@ class DownloaderAdapter extends RecyclerView.Adapter<DownloaderAdapter.ViewHolde
private MenuBottomSheetItem getCancelMenuItem()
{
return new MenuBottomSheetItem(R.string.cancel, R.drawable.ic_close, () -> onCancelActionSelected(mSelectedItem));
return new MenuBottomSheetItem(R.string.cancel, R.drawable.ic_cancel, () -> onCancelActionSelected(mSelectedItem));
}
private class ItemViewHolder extends BaseInnerViewHolder<CountryItem>

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, R.drawable.ic_search_fail);
placeholder.setContent(R.string.search_not_found, R.string.search_not_found_query);
else
placeholder.setContent(R.string.downloader_no_downloaded_maps_title,
R.string.downloader_no_downloaded_maps_message, R.drawable.ic_download);
R.string.downloader_no_downloaded_maps_message);
}
@Override

View File

@@ -4,7 +4,6 @@ import android.location.Location;
import android.text.TextUtils;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.Nullable;
import androidx.core.view.ViewCompat;
import app.organicmaps.MwmActivity;
@@ -27,10 +26,6 @@ public class OnmapDownloader implements MwmActivity.LeftAnimationTrackListener
{
private static boolean sAutodownloadLocked;
private static final int HIDE_THRESHOLD = 2;
// Default bundles (e.g., world/coasts). Used to approximate “user-downloaded” count.
private static final int DEFAULT_MAP_BASELINE = 2;
private final MwmActivity mActivity;
private final View mFrame;
private final MaterialTextView mParent;
@@ -38,7 +33,6 @@ public class OnmapDownloader implements MwmActivity.LeftAnimationTrackListener
private final MaterialTextView mSize;
private final WheelProgressView mProgress;
private final MaterialButton mButton;
private final View mOfflineExplanation;
private int mStorageSubscriptionSlot;
@@ -49,10 +43,8 @@ public class OnmapDownloader implements MwmActivity.LeftAnimationTrackListener
@Override
public void onStatusChanged(List<MapManager.StorageCallbackData> data)
{
if (mCurrentCountry == null) {
updateOfflineExplanationVisibility();
if (mCurrentCountry == null)
return;
}
for (MapManager.StorageCallbackData item : data)
{
@@ -66,7 +58,7 @@ public class OnmapDownloader implements MwmActivity.LeftAnimationTrackListener
{
mCurrentCountry.update();
updateProgressState(false);
updateOfflineExplanationVisibility();
return;
}
}
@@ -109,12 +101,6 @@ public class OnmapDownloader implements MwmActivity.LeftAnimationTrackListener
return enqueued || progress || applying;
}
private void updateOfflineExplanationVisibility() {
if (mOfflineExplanation == null) return;
// hide once threshold reached; safe to call repeatedly.
app.organicmaps.util.UiUtils.showIf(MapManager.nativeGetDownloadedCount() < (DEFAULT_MAP_BASELINE + HIDE_THRESHOLD), mOfflineExplanation);
}
private void updateProgressState(boolean shouldAutoDownload)
{
updateStateInternal(shouldAutoDownload);
@@ -122,8 +108,6 @@ public class OnmapDownloader implements MwmActivity.LeftAnimationTrackListener
private void updateStateInternal(boolean shouldAutoDownload)
{
updateOfflineExplanationVisibility();
boolean showFrame =
(mCurrentCountry != null && !mCurrentCountry.present && !RoutingController.get().isNavigating());
if (showFrame)
@@ -207,9 +191,6 @@ public class OnmapDownloader implements MwmActivity.LeftAnimationTrackListener
mProgress = controls.findViewById(R.id.wheel_downloader_progress);
mButton = controls.findViewById(R.id.downloader_button);
mOfflineExplanation = mFrame.findViewById(R.id.offline_explanation);
updateOfflineExplanationVisibility();
mProgress.setOnClickListener(v -> {
if (mCurrentCountry == null)
return;
@@ -266,7 +247,6 @@ public class OnmapDownloader implements MwmActivity.LeftAnimationTrackListener
public void onResume()
{
updateOfflineExplanationVisibility();
if (mStorageSubscriptionSlot == 0)
{
mStorageSubscriptionSlot = MapManager.nativeSubscribe(mStorageCallback);

View File

@@ -153,7 +153,6 @@ public class EditorFragment extends BaseMwmFragment implements View.OnClickListe
private final Map<Metadata.MetadataType, View> mDetailsBlocks = new HashMap<>();
private final Map<Metadata.MetadataType, View> mSocialMediaBlocks = new HashMap<>();
private MaterialButton mReset;
private MaterialButton mDisused;
private EditorHostFragment mParent;
@@ -353,8 +352,7 @@ public class EditorFragment extends BaseMwmFragment implements View.OnClickListe
{
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);
UiUtils.showIf(hasChargeSockets, mCardChargingStation);
setCardVisibility(mCardDetails, mDetailsBlocks, editableDetails);
setCardVisibility(mCardSocialMedia, mSocialMediaBlocks, editableDetails);
@@ -828,8 +826,6 @@ public class EditorFragment extends BaseMwmFragment implements View.OnClickListe
osmInfo.setMovementMethod(LinkMovementMethod.getInstance());
mReset = view.findViewById(R.id.reset);
mReset.setOnClickListener(this);
mDisused = view.findViewById(R.id.disused);
mDisused.setOnClickListener(this);
mDetailsBlocks.put(Metadata.MetadataType.FMD_OPEN_HOURS, blockOpeningHours);
mDetailsBlocks.put(Metadata.MetadataType.FMD_PHONE_NUMBER, blockPhone);
@@ -897,8 +893,6 @@ public class EditorFragment extends BaseMwmFragment implements View.OnClickListe
mParent.addLanguage();
else if (id == R.id.reset)
reset();
else if (id == R.id.disused)
placeDisused();
else if (id == R.id.block_outdoor_seating)
mOutdoorSeating.toggle();
}
@@ -944,12 +938,9 @@ public class EditorFragment extends BaseMwmFragment implements View.OnClickListe
if (mParent.addingNewObject())
{
UiUtils.hide(mReset);
UiUtils.hide(mDisused);
return;
}
mDisused.setVisibility(Editor.nativeCanMarkPlaceAsDisused() ? View.VISIBLE : View.GONE);
if (Editor.nativeIsMapObjectUploaded())
{
mReset.setText(R.string.editor_place_doesnt_exist);
@@ -1022,19 +1013,6 @@ public class EditorFragment extends BaseMwmFragment implements View.OnClickListe
dialogFragment.setTextSaveListener(this::commitPlaceDoesntExists);
}
private void placeDisused()
{
new MaterialAlertDialogBuilder(requireActivity(), R.style.MwmTheme_AlertDialog)
.setTitle(R.string.editor_mark_business_vacant_title)
.setMessage(R.string.editor_mark_business_vacant_description)
.setPositiveButton(R.string.editor_submit, (dlg, which) -> {
Editor.nativeMarkPlaceAsDisused();
mParent.processEditedFeatures();
})
.setNegativeButton(android.R.string.cancel, null)
.show();
}
private void commitPlaceDoesntExists(@NonNull String text)
{
Editor.nativePlaceDoesNotExist(text);

View File

@@ -358,7 +358,7 @@ public class EditorHostFragment
.show();
}
public void processEditedFeatures()
private void processEditedFeatures()
{
if (OsmOAuth.isAuthorized())
{

View File

@@ -1,208 +0,0 @@
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

@@ -0,0 +1,211 @@
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.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.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();
final AlertDialog dialog =
new MaterialAlertDialogBuilder(requireActivity(), R.style.MwmMain_DialogFragment_TimePicker)
.setView(root)
.setNegativeButton(R.string.cancel, null)
.setPositiveButton(R.string.ok, null)
.setCancelable(true)
.create();
dialog.setOnShowListener(dialogInterface -> {
mOkButton = dialog.getButton(AlertDialog.BUTTON_POSITIVE);
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(), 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

@@ -1,10 +1,7 @@
package app.organicmaps.editor;
import android.content.res.Configuration;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.core.os.ConfigurationCompat;
import androidx.core.os.LocaleListCompat;
import androidx.fragment.app.Fragment;
import app.organicmaps.base.BaseMwmRecyclerFragment;
import app.organicmaps.sdk.editor.Editor;
@@ -14,7 +11,6 @@ import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
public class LanguagesFragment extends BaseMwmRecyclerFragment<LanguagesAdapter>
@@ -36,23 +32,10 @@ public class LanguagesFragment extends BaseMwmRecyclerFragment<LanguagesAdapter>
Set<String> existingLanguages =
args != null ? new HashSet<>(args.getStringArrayList(EXISTING_LOCALIZED_NAMES)) : new HashSet<>();
Configuration config = requireContext().getResources().getConfiguration();
LocaleListCompat systemLocales = ConfigurationCompat.getLocales(config);
List<Language> languages = new ArrayList<>();
List<Language> systemLanguages = new ArrayList<>();
for (Language lang : Editor.nativeGetSupportedLanguages(false))
{
// Separately extract system languages
for (int i = 0; i < systemLocales.size(); i++)
{
Locale locale = systemLocales.get(i);
if (locale != null && locale.getLanguage().equals(lang.code))
systemLanguages.add(lang);
}
if (existingLanguages.contains(lang.code) || systemLanguages.contains(lang))
if (existingLanguages.contains(lang.code))
continue;
languages.add(lang);
@@ -60,8 +43,6 @@ public class LanguagesFragment extends BaseMwmRecyclerFragment<LanguagesAdapter>
Collections.sort(languages, Comparator.comparing(lhs -> lhs.name));
languages.addAll(0, systemLanguages);
return new LanguagesAdapter(this, languages.toArray(new Language[languages.size()]));
}

View File

@@ -113,6 +113,9 @@ 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

@@ -1,18 +1,15 @@
package app.organicmaps.editor;
import android.content.res.ColorStateList;
import android.util.SparseArray;
import android.view.LayoutInflater;
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;
import androidx.appcompat.widget.SwitchCompat;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.RecyclerView;
import app.organicmaps.R;
@@ -32,13 +29,13 @@ import java.util.Calendar;
import java.util.List;
class SimpleTimetableAdapter extends RecyclerView.Adapter<SimpleTimetableAdapter.BaseTimetableViewHolder>
implements FromToTimePicker.OnPickListener, TimetableProvider
implements HoursMinutesPickerFragment.OnPickListener, TimetableProvider
{
private static final int TYPE_TIMETABLE = 0;
private static final int TYPE_ADD_TIMETABLE = 1;
private static final int ID_OPENING_TIME = 0;
private static final int ID_CLOSED_SPAN = 1;
private static final int ID_OPENING = 0;
private static final int ID_CLOSING = 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};
@@ -72,7 +69,7 @@ class SimpleTimetableAdapter extends RecyclerView.Adapter<SimpleTimetableAdapter
@Override
public String getTimetables()
{
return OpeningHours.nativeTimetablesToString(mItems.toArray(new Timetable[0]));
return OpeningHours.nativeTimetablesToString(mItems.toArray(new Timetable[mItems.size()]));
}
@Override
@@ -104,7 +101,7 @@ class SimpleTimetableAdapter extends RecyclerView.Adapter<SimpleTimetableAdapter
private void addTimetable()
{
mItems.add(OpeningHours.nativeGetComplementTimetable(mItems.toArray(new Timetable[0])));
mItems.add(OpeningHours.nativeGetComplementTimetable(mItems.toArray(new Timetable[mItems.size()])));
notifyItemInserted(mItems.size() - 1);
refreshComplement();
}
@@ -118,31 +115,25 @@ class SimpleTimetableAdapter extends RecyclerView.Adapter<SimpleTimetableAdapter
private void refreshComplement()
{
mComplementItem = OpeningHours.nativeGetComplementTimetable(mItems.toArray(new Timetable[0]));
mComplementItem = OpeningHours.nativeGetComplementTimetable(mItems.toArray(new Timetable[mItems.size()]));
notifyItemChanged(getItemCount() - 1);
}
private void pickTime(int position,
@IntRange(from = ID_OPENING_TIME, to = ID_CLOSED_SPAN) int id,
boolean startWithToTime)
@IntRange(from = HoursMinutesPickerFragment.TAB_FROM, to = HoursMinutesPickerFragment.TAB_TO)
int tab, @IntRange(from = ID_OPENING, to = ID_CLOSING) int id)
{
final Timetable data = mItems.get(position);
mPickingPosition = position;
FromToTimePicker.pickTime(mFragment,
this,
data.workingTimespan.start,
data.workingTimespan.end,
id,
startWithToTime);
HoursMinutesPickerFragment.pick(mFragment.requireActivity(), mFragment.getChildFragmentManager(),
data.workingTimespan.start, data.workingTimespan.end, tab, id);
}
@Override
public void onHoursMinutesPicked(HoursMinutes from, HoursMinutes to, int id)
{
final Timetable item = mItems.get(mPickingPosition);
if (id == ID_OPENING_TIME)
if (id == ID_OPENING)
mItems.set(mPickingPosition, OpeningHours.nativeSetOpeningTime(item, new Timespan(from, to)));
else
mItems.set(mPickingPosition, OpeningHours.nativeAddClosedSpan(item, new Timespan(from, to)));
@@ -157,7 +148,7 @@ class SimpleTimetableAdapter extends RecyclerView.Adapter<SimpleTimetableAdapter
private void addWorkingDay(int day, int position)
{
final Timetable[] tts = mItems.toArray(new Timetable[0]);
final Timetable[] tts = mItems.toArray(new Timetable[mItems.size()]);
mItems = new ArrayList<>(Arrays.asList(OpeningHours.nativeAddWorkingDay(tts, position, day)));
refreshComplement();
notifyDataSetChanged();
@@ -165,7 +156,7 @@ class SimpleTimetableAdapter extends RecyclerView.Adapter<SimpleTimetableAdapter
private void removeWorkingDay(int day, int position)
{
final Timetable[] tts = mItems.toArray(new Timetable[0]);
final Timetable[] tts = mItems.toArray(new Timetable[mItems.size()]);
mItems = new ArrayList<>(Arrays.asList(OpeningHours.nativeRemoveWorkingDay(tts, position, day)));
refreshComplement();
notifyDataSetChanged();
@@ -271,13 +262,13 @@ class SimpleTimetableAdapter extends RecyclerView.Adapter<SimpleTimetableAdapter
{
final int id = v.getId();
if (id == R.id.time_open)
pickTime(getBindingAdapterPosition(), ID_OPENING_TIME, false);
pickTime(getBindingAdapterPosition(), HoursMinutesPickerFragment.TAB_FROM, ID_OPENING);
else if (id == R.id.time_close)
pickTime(getBindingAdapterPosition(), ID_OPENING_TIME, true);
pickTime(getBindingAdapterPosition(), HoursMinutesPickerFragment.TAB_TO, ID_OPENING);
else if (id == R.id.tv__remove_timetable)
removeTimetable(getBindingAdapterPosition());
else if (id == R.id.tv__add_closed)
pickTime(getBindingAdapterPosition(), ID_CLOSED_SPAN, false);
pickTime(getBindingAdapterPosition(), HoursMinutesPickerFragment.TAB_FROM, ID_CLOSING);
else if (id == R.id.allday)
swAllday.toggle();
}
@@ -383,29 +374,6 @@ class SimpleTimetableAdapter extends RecyclerView.Adapter<SimpleTimetableAdapter
final boolean enable = mComplementItem != null && mComplementItem.weekdays.length != 0;
final String text = mFragment.getString(R.string.editor_time_add);
mAdd.setEnabled(enable);
final ColorStateList bgButtonColor = new ColorStateList(
new int[][]{
new int[]{android.R.attr.state_enabled}, // enabled
new int[]{-android.R.attr.state_enabled} // disabled
},
new int[]{
ContextCompat.getColor(
mAdd.getContext(), R.color.base_accent),
ContextCompat.getColor(mAdd.getContext(), R.color.button_accent_disabled)
});
final ColorStateList textButtonColor = new ColorStateList(
new int[][]{
new int[]{android.R.attr.state_enabled}, // enabled
new int[]{-android.R.attr.state_enabled} // disabled
},
new int[]{
ContextCompat.getColor(
mAdd.getContext(),
UiUtils.getStyledResourceId(mAdd.getContext(), android.R.attr.textColorPrimaryInverse)),
ContextCompat.getColor(mAdd.getContext(), R.color.button_accent_text_disabled)
});
mAdd.setBackgroundTintList(bgButtonColor);
mAdd.setTextColor(textButtonColor);
mAdd.setText(enable ? text + " (" + TimeFormatUtils.formatWeekdays(mComplementItem) + ")" : text);
}
}

View File

@@ -8,9 +8,10 @@ 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
implements TimetableProvider, HoursMinutesPickerFragment.OnPickListener
{
private SimpleTimetableAdapter mAdapter;
@Nullable
@@ -56,4 +57,10 @@ 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

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

View File

@@ -21,9 +21,6 @@ import app.organicmaps.util.UiUtils;
class SearchAdapter extends RecyclerView.Adapter<SearchAdapter.SearchDataViewHolder>
{
private static final int SHORT_HORIZON_CLOSE_MIN = 60;
private static final int SHORT_HORIZON_OPEN_MIN = 15;
private final SearchFragment mSearchFragment;
@Nullable
private SearchResult[] mResults;
@@ -152,32 +149,41 @@ class SearchAdapter extends RecyclerView.Adapter<SearchAdapter.SearchDataViewHol
{
final Resources resources = mSearchFragment.getResources();
if (result.description.openNow != SearchResult.OPEN_NOW_YES && result.description.openNow != SearchResult.OPEN_NOW_NO)
switch (result.description.openNow)
{
// Hide if unknown opening hours state
UiUtils.hide(mOpen);
return;
case SearchResult.OPEN_NOW_YES ->
{
if (result.description.minutesUntilClosed < 60) // less than 1 hour
{
final String time = result.description.minutesUntilClosed + " " + resources.getString(R.string.minute);
final String string = resources.getString(R.string.closes_in, time);
UiUtils.setTextAndShow(mOpen, string);
mOpen.setTextColor(ContextCompat.getColor(mSearchFragment.getContext(), R.color.base_yellow));
}
else
{
UiUtils.setTextAndShow(mOpen, resources.getString(R.string.editor_time_open));
mOpen.setTextColor(ContextCompat.getColor(mSearchFragment.getContext(), R.color.base_green));
}
}
final boolean isOpen = result.description.openNow == SearchResult.OPEN_NOW_YES;
final int minsToNextState = isOpen ? result.description.minutesUntilClosed : result.description.minutesUntilOpen;
final boolean shortHorizonClosing = isOpen && minsToNextState >= 0 && minsToNextState <= SHORT_HORIZON_CLOSE_MIN;
final boolean shortHorizonOpening = !isOpen && minsToNextState >= 0 && minsToNextState <= SHORT_HORIZON_OPEN_MIN;
if (shortHorizonClosing || shortHorizonOpening)
case SearchResult.OPEN_NOW_NO ->
{
final String minsToChangeStr = resources.getQuantityString(
R.plurals.minutes_short, Math.max(minsToNextState, 1), Math.max(minsToNextState, 1));
final String nextChangeFormatted = resources.getString(isOpen ? R.string.closes_in : R.string.opens_in, minsToChangeStr);
if (result.description.minutesUntilOpen < 60) // less than 1 hour
{
final String time = result.description.minutesUntilOpen + " " + resources.getString(R.string.minute);
final String string = resources.getString(R.string.opens_in, time);
UiUtils.setTextAndShow(mOpen, nextChangeFormatted);
mOpen.setTextColor(ContextCompat.getColor(mSearchFragment.getContext(), R.color.base_yellow));
UiUtils.setTextAndShow(mOpen, string);
mOpen.setTextColor(ContextCompat.getColor(mSearchFragment.getContext(), R.color.base_red));
}
else
{
UiUtils.setTextAndShow(mOpen, resources.getString(R.string.closed));
mOpen.setTextColor(ContextCompat.getColor(mSearchFragment.getContext(), R.color.base_red));
}
}
else
{
UiUtils.setTextAndShow(mOpen, isOpen ? resources.getString(R.string.editor_time_open) : resources.getString(R.string.closed));
mOpen.setTextColor(ContextCompat.getColor(mSearchFragment.getContext(), isOpen ? R.color.base_green : R.color.base_red));
default -> UiUtils.hide(mOpen);
}
}

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, R.drawable.ic_search_fail);
mResultsPlaceholder.setContent(R.string.search_not_found, R.string.search_not_found_query);
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, R.drawable.ic_search_recent);
mPlaceHolder.setContent(R.string.search_history_title, R.string.search_history_text);
getAdapter().registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
@Override

View File

@@ -90,36 +90,28 @@ public class DrivingOptionsFragment extends BaseMwmToolbarFragment
{
SwitchCompat tollsBtn = root.findViewById(R.id.avoid_tolls_btn);
tollsBtn.setChecked(RoutingOptions.hasOption(RoadType.Toll));
CompoundButton.OnCheckedChangeListener tollBtnListener = new ToggleRoutingOptionListener(RoadType.Toll, root);
CompoundButton.OnCheckedChangeListener tollBtnListener = new ToggleRoutingOptionListener(RoadType.Toll);
tollsBtn.setOnCheckedChangeListener(tollBtnListener);
SwitchCompat motorwaysBtn = root.findViewById(R.id.avoid_motorways_btn);
motorwaysBtn.setChecked(RoutingOptions.hasOption(RoadType.Motorway));
CompoundButton.OnCheckedChangeListener motorwayBtnListener =
new ToggleRoutingOptionListener(RoadType.Motorway, root);
CompoundButton.OnCheckedChangeListener motorwayBtnListener = new ToggleRoutingOptionListener(RoadType.Motorway);
motorwaysBtn.setOnCheckedChangeListener(motorwayBtnListener);
SwitchCompat ferriesBtn = root.findViewById(R.id.avoid_ferries_btn);
ferriesBtn.setChecked(RoutingOptions.hasOption(RoadType.Ferry));
CompoundButton.OnCheckedChangeListener ferryBtnListener = new ToggleRoutingOptionListener(RoadType.Ferry, root);
CompoundButton.OnCheckedChangeListener ferryBtnListener = new ToggleRoutingOptionListener(RoadType.Ferry);
ferriesBtn.setOnCheckedChangeListener(ferryBtnListener);
SwitchCompat dirtyRoadsBtn = root.findViewById(R.id.avoid_dirty_roads_btn);
dirtyRoadsBtn.setChecked(RoutingOptions.hasOption(RoadType.Dirty));
dirtyRoadsBtn.setEnabled(!RoutingOptions.hasOption(RoadType.Paved) || RoutingOptions.hasOption(RoadType.Dirty));
CompoundButton.OnCheckedChangeListener dirtyBtnListener = new ToggleRoutingOptionListener(RoadType.Dirty, root);
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, root);
CompoundButton.OnCheckedChangeListener stepsBtnListener = new ToggleRoutingOptionListener(RoadType.Steps);
stepsBtn.setOnCheckedChangeListener(stepsBtnListener);
SwitchCompat pavedBtn = root.findViewById(R.id.avoid_paved_roads_btn);
pavedBtn.setChecked(RoutingOptions.hasOption(RoadType.Paved));
pavedBtn.setEnabled(!RoutingOptions.hasOption(RoadType.Dirty) || RoutingOptions.hasOption(RoadType.Paved));
CompoundButton.OnCheckedChangeListener pavedBtnListener = new ToggleRoutingOptionListener(RoadType.Paved, root);
pavedBtn.setOnCheckedChangeListener(pavedBtnListener);
}
private static class ToggleRoutingOptionListener implements CompoundButton.OnCheckedChangeListener
@@ -127,13 +119,9 @@ public class DrivingOptionsFragment extends BaseMwmToolbarFragment
@NonNull
private final RoadType mRoadType;
@NonNull
private final View mRoot;
private ToggleRoutingOptionListener(@NonNull RoadType roadType, @NonNull View root)
private ToggleRoutingOptionListener(@NonNull RoadType roadType)
{
mRoadType = roadType;
mRoot = root;
}
@Override
@@ -143,27 +131,6 @@ public class DrivingOptionsFragment extends BaseMwmToolbarFragment
RoutingOptions.addOption(mRoadType);
else
RoutingOptions.removeOption(mRoadType);
SwitchCompat dirtyRoadsBtn = mRoot.findViewById(R.id.avoid_dirty_roads_btn);
SwitchCompat pavedBtn = mRoot.findViewById(R.id.avoid_paved_roads_btn);
if (mRoadType == RoadType.Dirty)
{
pavedBtn.setEnabled(!isChecked);
if (isChecked)
{
pavedBtn.setChecked(false);
dirtyRoadsBtn.setEnabled(true);
}
}
else if (mRoadType == RoadType.Paved)
{
dirtyRoadsBtn.setEnabled(!isChecked);
if (isChecked)
{
dirtyRoadsBtn.setChecked(false);
pavedBtn.setEnabled(true);
}
}
}
}
}

View File

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

View File

@@ -0,0 +1,25 @@
package app.organicmaps.util;
import android.app.Activity;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.core.text.TextUtilsCompat;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
public class RtlUtils
{
private final static List<String> rtlLocalesWithTranslation = Arrays.asList("ar", "fa");
public static void manageRtl(@NonNull final Activity activity)
{
final String currentLanguage = Locale.getDefault().getLanguage();
final boolean isRTL =
TextUtilsCompat.getLayoutDirectionFromLocale(Locale.getDefault()) == View.LAYOUT_DIRECTION_RTL;
if (isRTL && rtlLocalesWithTranslation.contains(currentLanguage))
activity.getWindow().getDecorView().setLayoutDirection(View.LAYOUT_DIRECTION_RTL);
else
activity.getWindow().getDecorView().setLayoutDirection(View.LAYOUT_DIRECTION_LTR);
}
}

View File

@@ -3,7 +3,6 @@ package app.organicmaps.widget;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
@@ -12,7 +11,6 @@ import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.core.content.ContextCompat;
import com.google.android.material.imageview.ShapeableImageView;
import com.google.android.material.textview.MaterialTextView;
@@ -178,10 +176,9 @@ public class PlaceholderView extends LinearLayout
return view.getMeasuredHeight() + params.bottomMargin + params.topMargin;
}
public void setContent(@StringRes int titleRes, @StringRes int subtitleRes, @DrawableRes int iconRes)
public void setContent(@StringRes int titleRes, @StringRes int subtitleRes)
{
mTitle.setText(titleRes);
mSubtitle.setText(subtitleRes);
mImage.setImageDrawable(ContextCompat.getDrawable(getContext(), iconRes));
}
}

View File

@@ -1,6 +1,5 @@
package app.organicmaps.widget.placepage;
import android.app.Dialog;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
@@ -15,7 +14,6 @@ import androidx.annotation.Nullable;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowCompat;
import androidx.core.view.WindowInsetsCompat;
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.FragmentFactory;
import androidx.fragment.app.FragmentManager;
import app.organicmaps.R;
@@ -105,9 +103,9 @@ public class EditBookmarkFragment extends BaseMwmDialogFragment implements View.
public EditBookmarkFragment() {}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setStyle(DialogFragment.STYLE_NORMAL, R.style.MwmTheme_FullScreenDialog);
protected int getCustomTheme()
{
return getFullscreenTheme();
}
@Nullable
@@ -183,12 +181,6 @@ public class EditBookmarkFragment extends BaseMwmDialogFragment implements View.
public void onStart()
{
super.onStart();
Dialog dialog = getDialog();
if (dialog != null) {
dialog.getWindow().setLayout(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT);
}
// Focus name and show keyboard for "Unknown Place" bookmarks
if (mBookmark != null

View File

@@ -1,49 +0,0 @@
package app.organicmaps.widget.placepage;
import java.time.ZonedDateTime;
import java.time.format.TextStyle;
import java.util.Locale;
public class OpenStateTextFormatter
{
private OpenStateTextFormatter() {}
static String formatHoursMinutes(int hour, int minute, boolean use24h)
{
if (use24h)
return String.format(Locale.ROOT, "%02d:%02d", hour, minute);
int h = hour % 12;
if (h == 0) h = 12;
String ampm = (hour < 12) ? "AM" : "PM";
return String.format(Locale.ROOT, "%d:%02d %s", h, minute, ampm);
}
static boolean isSameLocalDate(ZonedDateTime a, ZonedDateTime b)
{
return a.toLocalDate().isEqual(b.toLocalDate());
}
static String dayShort(ZonedDateTime t, Locale locale)
{
return t.getDayOfWeek().getDisplayName(TextStyle.SHORT, locale);
}
static String buildAtLabel(
boolean opens,
boolean isToday,
String dayShort,
String time,
String opensAtLocalized,
String closesAtLocalized,
String opensDayAtLocalized,
String closesDayAtLocalized
)
{
if (isToday)
return opens ? String.format(Locale.ROOT, opensAtLocalized, time) // Opens at %s
: String.format(Locale.ROOT, closesAtLocalized, time); // Closes at %s
return opens ? String.format(Locale.ROOT, opensDayAtLocalized, dayShort, time) // Opens %s at %s
: String.format(Locale.ROOT, closesDayAtLocalized, dayShort, time); // Closes %s at %s
}
}

View File

@@ -31,8 +31,6 @@ import androidx.fragment.app.FragmentFactory;
import androidx.fragment.app.FragmentManager;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
import app.organicmaps.BuildConfig;
import app.organicmaps.MwmActivity;
import app.organicmaps.MwmApplication;
import app.organicmaps.R;
@@ -87,11 +85,9 @@ import com.google.android.material.textview.MaterialTextView;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.TextStyle;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
public class PlacePageView extends Fragment
implements View.OnClickListener, View.OnLongClickListener, LocationListener, SensorListener, Observer<MapObject>,
@@ -109,9 +105,6 @@ public class PlacePageView extends Fragment
private static final String LINKS_FRAGMENT_TAG = "LINKS_FRAGMENT_TAG";
private static final String TRACK_SHARE_MENU_ID = "TRACK_SHARE_MENU_ID";
private static final int SHORT_HORIZON_CLOSE_MIN = 60;
private static final int SHORT_HORIZON_OPEN_MIN = 15;
private static final List<CoordinatesFormat> visibleCoordsFormat =
Arrays.asList(CoordinatesFormat.LatLonDMS, CoordinatesFormat.LatLonDecimal, CoordinatesFormat.OLCFull,
CoordinatesFormat.UTM, CoordinatesFormat.MGRS, CoordinatesFormat.OSMLink);
@@ -159,7 +152,7 @@ public class PlacePageView extends Fragment
private View mEditTopSpace;
private ShapeableImageView mColorIcon;
private MaterialTextView mTvCategory;
private MaterialButton mEditBookmark;
private ShapeableImageView mEditBookmark;
// Data
private CoordinatesFormat mCoordsFormat = CoordinatesFormat.LatLonDecimal;
@@ -698,27 +691,16 @@ public class PlacePageView extends Fragment
mTvAddPlace.setOnClickListener(this);
mTvEditPlace.setEnabled(Editor.nativeShouldEnableEditPlace());
mTvAddPlace.setEnabled(Editor.nativeShouldEnableAddPlace());
final int editTextButtonColor =
final int editPlaceButtonColor =
Editor.nativeShouldEnableEditPlace()
? ContextCompat.getColor(
getContext(),
UiUtils.getStyledResourceId(getContext(), com.google.android.material.R.attr.colorSecondary))
: ContextCompat.getColor(getContext(), R.color.button_accent_text_disabled);
final ColorStateList editStrokeButtonColor = new ColorStateList(
new int[][]{
new int[]{android.R.attr.state_enabled}, // enabled
new int[]{-android.R.attr.state_enabled} // disabled
},
new int[]{
ContextCompat.getColor(
getContext(),
UiUtils.getStyledResourceId(getContext(), com.google.android.material.R.attr.colorSecondary)),
ContextCompat.getColor(getContext(), R.color.button_accent_text_disabled)
});
mTvEditPlace.setTextColor(editTextButtonColor);
mTvAddPlace.setTextColor(editTextButtonColor);
mTvEditPlace.setStrokeColor(editStrokeButtonColor);
mTvAddPlace.setStrokeColor(editStrokeButtonColor);
mTvEditPlace.setTextColor(editPlaceButtonColor);
mTvAddPlace.setTextColor(editPlaceButtonColor);
mTvEditPlace.setStrokeColor(ColorStateList.valueOf(editPlaceButtonColor));
mTvAddPlace.setStrokeColor(ColorStateList.valueOf(editPlaceButtonColor));
UiUtils.showIf(
UiUtils.isVisible(mEditPlace) || UiUtils.isVisible(mAddPlace),
mEditTopSpace);
@@ -804,127 +786,57 @@ public class PlacePageView extends Fragment
final String ohStr = mMapObject.getMetadata(Metadata.MetadataType.FMD_OPEN_HOURS);
final Timetable[] timetables = OpeningHours.nativeTimetablesFromString(ohStr);
// No valid timetable
if (timetables == null || timetables.length == 0)
if (timetables != null && timetables.length != 0)
{
UiUtils.hide(mTvOpenState);
return;
}
final Context context = requireContext();
final OhState poiState = OpeningHours.nativeCurrentState(timetables);
final Context context = requireContext();
final OhState poiState = OpeningHours.nativeCurrentState(timetables);
// Ignore unknown rule state
if (poiState.state == OhState.State.Unknown)
{
UiUtils.hide(mTvOpenState);
return;
}
// Get colours
final ForegroundColorSpan colorGreen =
new ForegroundColorSpan(ContextCompat.getColor(context, R.color.base_green));
final ForegroundColorSpan colorYellow =
new ForegroundColorSpan(ContextCompat.getColor(context, R.color.base_yellow));
final ForegroundColorSpan colorRed = new ForegroundColorSpan(ContextCompat.getColor(context, R.color.base_red));
// Get next state info
final SpannableStringBuilder openStateString = new SpannableStringBuilder();
final boolean isOpen = (poiState.state == OhState.State.Open); // False == Closed due to early exit for Unknown
final long nextStateTime = isOpen ? poiState.nextTimeClosed : poiState.nextTimeOpen; // Unix time (seconds)
ZonedDateTime nextChangeLocal = null;
boolean hasFiniteNextChange = false;
final long nowSec = System.currentTimeMillis() / 1000;
final int minsToNextState = (int) ((nextStateTime - nowSec) / 60);
// Try to resolve a finite next-change time; handle 24/7 case
final boolean looksLike247 = "24/7".equals(ohStr.trim());
final int ONE_WEEK_MIN = 7 * 24 * 60;
final boolean noRealNextChange = looksLike247 || minsToNextState >= ONE_WEEK_MIN;
if (!noRealNextChange)
{
try
// Ignore unknown rule state
if (poiState.state == OhState.State.Unknown)
{
if (nextStateTime > 0 && nextStateTime < Long.MAX_VALUE / 2)
{
// NOTE: Timezone is currently device timezone. TODO: use feature-specific timezone.
nextChangeLocal = ZonedDateTime.ofInstant(
Instant.ofEpochSecond(nextStateTime), ZoneId.systemDefault()
);
hasFiniteNextChange = true;
}
UiUtils.hide(mTvOpenState);
return;
}
catch (Throwable ignored) {}
}
if (!hasFiniteNextChange) // No valid next change
{
if (isOpen)
// Get colours
final ForegroundColorSpan colorGreen =
new ForegroundColorSpan(ContextCompat.getColor(context, R.color.base_green));
final ForegroundColorSpan colorYellow =
new ForegroundColorSpan(ContextCompat.getColor(context, R.color.base_yellow));
final ForegroundColorSpan colorRed = new ForegroundColorSpan(ContextCompat.getColor(context, R.color.base_red));
// Get next state info
final SpannableStringBuilder openStateString = new SpannableStringBuilder();
final boolean isOpen = (poiState.state == OhState.State.Open); // False == Closed due to early exit for Unknown
final long nextStateTime = isOpen ? poiState.nextTimeClosed : poiState.nextTimeOpen; // Unix time (seconds)
final int minsToNextState = (int) ((nextStateTime - (System.currentTimeMillis() / 1000)) / 60);
if (minsToNextState <= 60) // POI opens/closes in 60 mins
{
final String minsToChangeStr = minsToNextState + " " + getString(R.string.minute);
final String nextChangeFormatted = getString(isOpen ? R.string.closes_in : R.string.opens_in, minsToChangeStr);
final ForegroundColorSpan nextChangeColor = isOpen ? colorYellow : colorRed;
// TODO: We should check closed/open time for specific feature's timezone.
ZonedDateTime time = ZonedDateTime.ofInstant(Instant.ofEpochSecond(nextStateTime), ZoneId.systemDefault());
String localizedTime =
new HoursMinutes(time.getHour(), time.getMinute(), DateUtils.is24HourFormat(context)).toString();
openStateString.append(nextChangeFormatted, nextChangeColor, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
.append("") // Add spacer
.append(getString(R.string.at, localizedTime));
}
else if (isOpen)
openStateString.append(getString(R.string.open_now), colorGreen, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
else
// TODO: Add "Closes at 18:00" etc
else // Closed
openStateString.append(getString(R.string.closed_now), colorRed, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
// TODO: Add "Opens at 18:00" etc
UiUtils.setTextAndHideIfEmpty(mTvOpenState, openStateString);
return;
}
String localizedTimeString = OpenStateTextFormatter.formatHoursMinutes(
nextChangeLocal.getHour(), nextChangeLocal.getMinute(), DateUtils.is24HourFormat(context));
final boolean shortHorizonClosing = isOpen && minsToNextState >= 0 && minsToNextState <= SHORT_HORIZON_CLOSE_MIN;
final boolean shortHorizonOpening = !isOpen && minsToNextState >= 0 && minsToNextState <= SHORT_HORIZON_OPEN_MIN;
if (shortHorizonClosing || shortHorizonOpening) // POI Opens/Closes in 60 mins • at 18:00
{
final String minsToChangeStr = getResources().getQuantityString(
R.plurals.minutes_short, Math.max(minsToNextState, 1), Math.max(minsToNextState, 1));
final String nextChangeFormatted = getString(isOpen ? R.string.closes_in : R.string.opens_in, minsToChangeStr);
openStateString.append(nextChangeFormatted, colorYellow, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
.append("") // Add spacer
.append(getString(R.string.at, localizedTimeString));
}
else
{
final String opensAtStr = getString(R.string.opens_at); // "Opens at %s"
final String closesAtStr = getString(R.string.closes_at); // "Closes at %s"
final String opensDayAtStr = getString(R.string.opens_day_at); // "Opens %1$s at %2$s"
final String closesDayAtStr = getString(R.string.closes_day_at); // "Closes %1$s at %2$s"
final boolean isToday =
OpenStateTextFormatter.isSameLocalDate(nextChangeLocal, ZonedDateTime.now(nextChangeLocal.getZone()));
// Full weekday name per design feedback.
final String dayName =
nextChangeLocal.getDayOfWeek().getDisplayName(TextStyle.FULL, Locale.getDefault());
if (isOpen) // > 60 minutes OR negative (safety). Show “Open now • Closes at 18:00”
{
openStateString.append(getString(R.string.open_now), colorGreen, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
final String atLabel =
OpenStateTextFormatter.buildAtLabel(false, isToday, dayName, localizedTimeString,
opensAtStr, closesAtStr, opensDayAtStr, closesDayAtStr);
if (!TextUtils.isEmpty(atLabel))
openStateString.append("").append(atLabel);
}
else // Closed
{
openStateString.append(getString(R.string.closed_now), colorRed, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
final String atLabel =
OpenStateTextFormatter.buildAtLabel(true, isToday, dayName, localizedTimeString,
opensAtStr, closesAtStr, opensDayAtStr, closesDayAtStr);
if (!TextUtils.isEmpty(atLabel))
openStateString.append("").append(atLabel);
}
}
UiUtils.setTextAndHideIfEmpty(mTvOpenState, openStateString);
// No valid timetable
UiUtils.hide(mTvOpenState);
}
private void addPlace()

View File

@@ -59,6 +59,8 @@ public class PlacePageBookmarkFragment extends Fragment implements View.OnClickL
mFrame = view;
mTvBookmarkNote = mFrame.findViewById(R.id.tv__bookmark_notes);
mTvBookmarkNote.setOnLongClickListener(this);
final View editBookmarkBtn = mFrame.findViewById(R.id.tv__bookmark_edit);
editBookmarkBtn.setOnClickListener(this);
}
private void initWebView()

Binary file not shown.

After

Width:  |  Height:  |  Size: 520 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 620 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 594 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 338 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 414 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 402 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 B

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