Compare commits
201 Commits
yannikblos
...
x7z4w-map
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2f9ce76c28 | ||
|
|
ffe07d3337 | ||
|
|
546383d755 | ||
|
|
bf30165b5f | ||
|
|
0288b97b13 | ||
|
|
7e561d09d3 | ||
|
|
298518ae72 | ||
|
|
3bad6d25f0 | ||
|
|
d36361d669 | ||
|
|
688e20b1a6 | ||
|
|
85462161b2 | ||
|
|
b929823f6b | ||
|
|
22dd799585 | ||
|
|
6864d101e2 | ||
|
|
4bfb62b373 | ||
|
|
f1cf844986 | ||
|
|
f20c3bf50c | ||
|
|
e7cc602904 | ||
|
|
d473361e54 | ||
|
|
76d58e4a05 | ||
|
|
7b5878b010 | ||
|
|
3fabbae3f7 | ||
|
|
0add23fcf2 | ||
|
|
08bcb574fa | ||
|
|
db888f33c5 | ||
|
|
4a96d219f0 | ||
|
|
c2bc6c27aa | ||
|
|
1095e5dbc3 | ||
|
|
a1cbcc5885 | ||
|
|
641f2308c6 | ||
|
|
f858ebcce0 | ||
|
|
eb376f5afc | ||
|
|
71b47719af | ||
|
|
4f7230fcbe | ||
|
|
2dafdd4338 | ||
|
|
0237751afe | ||
|
|
e7fb3a2f2c | ||
|
|
e08d60bb40 | ||
|
|
de4252f86c | ||
|
|
9d87d77055 | ||
|
|
c88f59eb75 | ||
|
|
9b5c700ad8 | ||
|
|
7d5e6fabcd | ||
|
|
ebe0364030 | ||
|
|
43e7e1eb2e | ||
|
|
ce9af79a68 | ||
|
|
b54b77bce6 | ||
|
|
038dca9c6f | ||
|
|
47db332f09 | ||
|
|
fdb665317f | ||
|
|
2ac80f7a90 | ||
|
|
c759b4f4a7 | ||
|
|
38802011f8 | ||
|
|
ea887c3209 | ||
|
|
7f075d9bef | ||
|
|
5e576177ea | ||
|
|
2efc001001 | ||
|
|
afa968c7d3 | ||
|
|
22d7cf3969 | ||
|
|
acdcfe62a4 | ||
|
|
67d8249e5a | ||
|
|
a5174623c2 | ||
|
|
c6ac2919fa | ||
|
|
7174d697d4 | ||
|
|
07fb3c0055 | ||
|
|
3b0b4520a1 | ||
|
|
eaa56e5127 | ||
|
|
3f7dc91b5b | ||
|
|
48e1c1f3a5 | ||
|
|
e862da648e | ||
|
|
6d0111b434 | ||
|
|
610737d295 | ||
|
|
26cb42651c | ||
|
|
a702989b09 | ||
|
|
bf6f57d336 | ||
|
|
6b76e9826b | ||
|
|
e19e63930d | ||
|
|
92c2945897 | ||
|
|
2f343d3fba | ||
|
|
955d3702ac | ||
|
|
1d087ca854 | ||
|
|
2c37e22f5c | ||
|
|
2954b3b871 | ||
|
|
c22bc75fb0 | ||
|
|
45094b0c38 | ||
|
|
f4a775a2f9 | ||
|
|
25d84b4428 | ||
|
|
f98a0efa46 | ||
|
|
e53532ab0b | ||
|
|
df1d4bf67e | ||
|
|
6d89a4346d | ||
|
|
971c19a88d | ||
|
|
ff5ae33f2a | ||
|
|
d5f640c6d0 | ||
|
|
72920bb930 | ||
|
|
3b4ab0da89 | ||
|
|
90ee9e3c0f | ||
|
|
54c1aeba1e | ||
|
|
399908b97c | ||
|
|
1df7848888 | ||
|
|
84f7687b98 | ||
|
|
a6057af12d | ||
|
|
4b66d56978 | ||
|
|
6adf01f8de | ||
|
|
2c526d34e9 | ||
|
|
e58fe72250 | ||
|
|
698afc4880 | ||
|
|
20a688505a | ||
|
|
b8e77a0404 | ||
|
|
07cd1ec4f5 | ||
|
|
9b42b08673 | ||
|
|
47efaa77d3 | ||
|
|
ec14b3bb85 | ||
|
|
1612e6045f | ||
|
|
d20144d4f6 | ||
|
|
06ecf4e54a | ||
|
|
0bf9dad343 | ||
|
|
12bd86d26d | ||
|
|
5564c449b3 | ||
|
|
96782ad5b1 | ||
|
|
67938cdf31 | ||
|
|
0e5fa5c501 | ||
|
|
101faeb2aa | ||
|
|
6f9ea8a758 | ||
|
|
176b11003f | ||
|
|
ed0728a332 | ||
|
|
f3b105ee33 | ||
|
|
dd106df592 | ||
|
|
c25552ce03 | ||
|
|
40b0023046 | ||
|
|
efe4570adf | ||
|
|
b72d747a5e | ||
|
|
1a95097fbb | ||
|
|
0243b1e86b | ||
|
|
8f3978e391 | ||
|
|
680d97bc4f | ||
|
|
5683606c31 | ||
|
|
3c7eb92b17 | ||
|
|
226b0f03c8 | ||
|
|
0a3a4ebd9a | ||
|
|
a62f6c0ef6 | ||
|
|
ef280c7f89 | ||
|
|
c49c414ec4 | ||
|
|
e62196798f | ||
|
|
c687c850b8 | ||
|
|
f549358f28 | ||
|
|
e3c8e422d5 | ||
|
|
f664138a42 | ||
|
|
6ae28a0ccf | ||
|
|
6cef8e3594 | ||
|
|
ec76982895 | ||
|
|
17fb4dd855 | ||
|
|
8c880f00b2 | ||
|
|
6c02e1d53a | ||
|
|
a1944435ae | ||
|
|
5beed2672f | ||
|
|
64eb8af3c1 | ||
|
|
316e259ebb | ||
|
|
dce50b2ca6 | ||
|
|
8db1dd55b5 | ||
|
|
14c4d08e32 | ||
|
|
2ae482de76 | ||
|
|
5c2e0b5b43 | ||
|
|
b2077ecf0b | ||
|
|
68ee3f4cda | ||
|
|
07ba709939 | ||
|
|
9bfebc2046 | ||
|
|
24b498e386 | ||
|
|
0a0bb61942 | ||
|
|
d78fe108ad | ||
|
|
4aa441101c | ||
|
|
2d275d9148 | ||
|
|
0814b574a9 | ||
|
|
b4abce822e | ||
|
|
2e0443097a | ||
|
|
f6426fe689 | ||
|
|
6296de6ce9 | ||
|
|
4f63c5fdcf | ||
|
|
e4648fbc1f | ||
|
|
1de35bb5f8 | ||
|
|
7b7df6ff2e | ||
|
|
33e2f4854e | ||
|
|
5b4fa55e83 | ||
|
|
83256c4895 | ||
|
|
94542456a2 | ||
|
|
dd620c3f0c | ||
|
|
a42db17858 | ||
|
|
738d0641ca | ||
|
|
4f5f8782fe | ||
|
|
a886270dda | ||
|
|
66609ff08b | ||
|
|
c8bfeb8e96 | ||
|
|
7fc5ed494b | ||
|
|
d9850f506a | ||
|
|
f16d14e07f | ||
|
|
7852cdb5a5 | ||
|
|
9a96096066 | ||
|
|
f72c4a28d9 | ||
|
|
68bb78b00d | ||
|
|
b9d4f082de | ||
|
|
7e40a0e642 |
@@ -2,44 +2,130 @@ name: map-generator
|
||||
on:
|
||||
workflow_dispatch: # Manual trigger
|
||||
inputs:
|
||||
jobs:
|
||||
description: 'Which job(s) to run right now?'
|
||||
required: true
|
||||
default: 'all'
|
||||
type: choice
|
||||
options:
|
||||
- all
|
||||
- copy-coasts
|
||||
- planet
|
||||
- wiki
|
||||
- isolines
|
||||
- subways
|
||||
- tiger
|
||||
- maps
|
||||
map-generator-test:
|
||||
description: 'Test (non-prod) generation?'
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
# run-copy-coasts:
|
||||
# description: 'Copy last used coastlines?'
|
||||
# required: false
|
||||
# default: true
|
||||
# type: boolean
|
||||
run-isolines:
|
||||
description: 'Update altitude isolines?'
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
run-tiger:
|
||||
description: 'Update TIGER address data?'
|
||||
required: false
|
||||
default: true
|
||||
type: boolean
|
||||
run-planet-pbf:
|
||||
description: 'Update PBF planet (for Wiki & subways)?'
|
||||
required: false
|
||||
default: true
|
||||
type: boolean
|
||||
run-subways:
|
||||
description: 'Update subways?'
|
||||
required: false
|
||||
default: true
|
||||
type: boolean
|
||||
run-wiki:
|
||||
description: 'Update Wikipedia descriptions?'
|
||||
required: false
|
||||
default: true
|
||||
type: boolean
|
||||
run-planet-o5m:
|
||||
description: 'Update O5M planet (for mapgen)?'
|
||||
required: false
|
||||
default: true
|
||||
type: boolean
|
||||
run-mapgen:
|
||||
description: 'Run maps generation?'
|
||||
required: false
|
||||
default: true
|
||||
type: boolean
|
||||
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
|
||||
run-upload:
|
||||
description: 'Upload latest maps to CDN?'
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
# 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 }}
|
||||
S3_KEY_ID: ${{ secrets.S3_KEY_ID }}
|
||||
S3_SECRET_KEY: ${{ secrets.S3_SECRET_KEY }}
|
||||
S3_ENDPOINT: ${{ secrets.S3_ENDPOINT }}
|
||||
S3_BUCKET: ${{ secrets.S3_BUCKET }}
|
||||
SFTP_USER: ${{ secrets.SFTP_USER }}
|
||||
SFTP_PASSWORD: ${{ secrets.SFTP_PASSWORD }}
|
||||
SFTP_HOST: ${{ secrets.SFTP_HOST }}
|
||||
SFTP_PATH: ${{ secrets.SFTP_PATH }}
|
||||
ZULIP_BOT_EMAIL: ${{ secrets.ZULIP_BOT_EMAIL }}
|
||||
ZULIP_API_KEY: ${{ secrets.ZULIP_API_KEY }}
|
||||
MWMTEST: ${{ inputs.map-generator-test }}
|
||||
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 --depth 1 --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 --depth 1 --single-branch https://codeberg.org/comaps/wikiparser.git
|
||||
- name: Checkout subways repo
|
||||
shell: bash
|
||||
run: |
|
||||
cd ~
|
||||
git clone --depth 1 --single-branch https://codeberg.org/comaps/subways.git
|
||||
|
||||
copy-coasts:
|
||||
if: inputs.jobs == 'copy-coasts' || inputs.jobs == 'all'
|
||||
# if: inputs.run-copy-coasts
|
||||
name: Copy Previously Generated Coasts
|
||||
runs-on: mapfilemaker
|
||||
container:
|
||||
image: ubuntu:latest
|
||||
image: codeberg.org/comaps/maps_generator:f6d53d54f794
|
||||
volumes:
|
||||
- /media/4tbexternal:/media/4tbexternal
|
||||
- /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
|
||||
@@ -47,336 +133,445 @@ jobs:
|
||||
- name: Copy Coasts
|
||||
shell: bash
|
||||
run: |
|
||||
if [ -f /media/4tbexternal/osm-maps/*/intermediate_data/WorldCoasts.geom ]; then
|
||||
cp /media/4tbexternal/osm-maps/*/intermediate_data/WorldCoasts.geom /media/4tbexternal/osm-planet/latest_coasts.geom
|
||||
cp /media/4tbexternal/osm-maps/*/intermediate_data/WorldCoasts.rawgeom /media/4tbexternal/osm-planet/latest_coasts.rawgeom
|
||||
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*
|
||||
# TODO: don't copy coasts from test generations
|
||||
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'
|
||||
name: Update Planet
|
||||
update-isolines:
|
||||
if: inputs.run-isolines
|
||||
name: Update Isolines
|
||||
runs-on: mapfilemaker
|
||||
needs:
|
||||
- clone-repos
|
||||
container:
|
||||
image: ubuntu:latest
|
||||
image: codeberg.org/comaps/maps_generator:f6d53d54f794
|
||||
volumes:
|
||||
- /media/4tbexternal:/media/4tbexternal
|
||||
- /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: Install dependencies
|
||||
- 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: |
|
||||
apt-get update -y
|
||||
apt-get install -y pyosmium osmium-tool python3-venv python3-pip wget2
|
||||
rm -f /usr/lib/python*/EXTERNALLY-MANAGED
|
||||
pip3 install "protobuf<4"
|
||||
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-tiger:
|
||||
if: inputs.run-tiger
|
||||
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
|
||||
|
||||
update-planet-pbf:
|
||||
if: inputs.run-planet-pbf
|
||||
name: Update PBF Planet
|
||||
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: Download Planet File if Absent
|
||||
shell: bash
|
||||
# TODO: replace wget2 with curl -Z
|
||||
run: |
|
||||
if [ ! -d /media/4tbexternal/osm-planet/planet/ ]; then
|
||||
mkdir -p /media/4tbexternal/osm-planet/planet/
|
||||
if [ ! -d /home/planet/planet/ ]; then
|
||||
mkdir -p /home/planet/planet/
|
||||
fi
|
||||
if [ ! -f /media/4tbexternal/osm-planet/planet/planet-latest.osm.pbf ]; then
|
||||
cd /media/4tbexternal/osm-planet/planet/
|
||||
wget2 --verbose --progress=bar --continue --debug https://ftpmirror.your.org/pub/openstreetmap/pbf/planet-latest.osm.pbf
|
||||
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
|
||||
- name: Update PBF Planet
|
||||
shell: bash
|
||||
run: |
|
||||
cd /media/4tbexternal/osm-planet/planet/
|
||||
pyosmium-up-to-date planet-latest.osm.pbf -o planet-latest-new.osm.pbf -vv --size 16384
|
||||
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
|
||||
run: /root/OM/osmctools/osmconvert planet-latest.osm.pbf -o=planet.o5m
|
||||
- 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=PBF planet update is done!'
|
||||
|
||||
wiki-update:
|
||||
if: inputs.jobs == 'wiki' || inputs.jobs == 'all'
|
||||
name: Update Wikipedia
|
||||
update-subways:
|
||||
if: inputs.run-subways
|
||||
name: Update Subways
|
||||
runs-on: mapfilemaker
|
||||
needs:
|
||||
- clone-repos
|
||||
container:
|
||||
image: ubuntu:latest
|
||||
image: codeberg.org/comaps/maps_generator:f6d53d54f794
|
||||
volumes:
|
||||
- /media/4tbexternal:/media/4tbexternal
|
||||
- /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: Install dependencies
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: "~"
|
||||
key: cache-${{ github.run_id }}-${{ github.run_attempt }}
|
||||
- name: Update Subways
|
||||
shell: bash
|
||||
run: |
|
||||
apt-get update -y
|
||||
apt-get install -y jq curl wget2 rustc cargo git ca-certificates
|
||||
- name: Clone wikiparser if necessary
|
||||
shell: bash
|
||||
cd ~/comaps/
|
||||
cp tools/unix/maps/settings.sh.prod tools/unix/maps/settings.sh
|
||||
./tools/unix/maps/generate_subways.sh
|
||||
- name: Notify Zulip
|
||||
run: |
|
||||
if [ ! -d /media/4tbexternal/wikiparser ]; then
|
||||
cd /media/4tbexternal
|
||||
git clone https://codeberg.org/comaps/wikiparser.git
|
||||
fi
|
||||
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!'
|
||||
|
||||
wiki-update:
|
||||
if: inputs.run-wiki
|
||||
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 /media/4tbexternal/osm-planet/planet/planet-latest.osm.pbf ]; then
|
||||
echo "ERROR: No file at /media/4tbexternal/osm-planet/planet/planet-latest.osm.pbf"
|
||||
ls -al /media/4tbexternal/
|
||||
ls -al /media/4tbexternal/osm-planet/
|
||||
ls -al /media/4tbexternal/osm-planet/planet/
|
||||
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: |
|
||||
mkdir -p /media/4tbexternal/osm-planet/wikipedia/dumps
|
||||
mkdir -p /media/4tbexternal/osm-planet/wikipedia/build
|
||||
cd /media/4tbexternal/wikiparser
|
||||
#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 /media/4tbexternal/osm-planet/wikipedia/dumps
|
||||
./download.sh /home/planet/wikipedia/dumps
|
||||
ls -al /home/planet/wikipedia/dumps/*
|
||||
echo "Running ..."
|
||||
./run.sh /media/4tbexternal/osm-planet/wikipedia/build \
|
||||
/media/4tbexternal/osm-planet/planet/planet-latest.osm.pbf \
|
||||
/media/4tbexternal/osm-planet/wikipedia/dumps/latest/*.tar.gz
|
||||
./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
|
||||
|
||||
update-isolines:
|
||||
if: inputs.jobs == 'isolines' || inputs.jobs == 'all'
|
||||
name: Update Isolines
|
||||
# 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-planet-o5m:
|
||||
if: inputs.run-planet-o5m
|
||||
name: Update O5M Planet
|
||||
runs-on: mapfilemaker
|
||||
container:
|
||||
image: ubuntu:latest
|
||||
image: codeberg.org/comaps/maps_generator:f6d53d54f794
|
||||
volumes:
|
||||
- /media/4tbexternal:/media/4tbexternal
|
||||
- /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: Install dependencies
|
||||
- name: Check for O5M Planet File
|
||||
shell: bash
|
||||
run: |
|
||||
apt-get update -qq \
|
||||
&& apt-get install -y --no-install-recommends \
|
||||
curl \
|
||||
osmctools \
|
||||
rclone \
|
||||
git \
|
||||
ca-certificates \
|
||||
openssh-client \
|
||||
sshpass \
|
||||
vim \
|
||||
wget \
|
||||
build-essential \
|
||||
clang \
|
||||
cmake \
|
||||
python3 \
|
||||
python3-pip \
|
||||
python3.12-venv \
|
||||
qt6-base-dev \
|
||||
qt6-positioning-dev \
|
||||
libc++-dev \
|
||||
libfreetype-dev \
|
||||
libglvnd-dev \
|
||||
libgl1-mesa-dev \
|
||||
libharfbuzz-dev \
|
||||
libicu-dev \
|
||||
libqt6svg6-dev \
|
||||
libqt6positioning6-plugins \
|
||||
libqt6positioning6 \
|
||||
libsqlite3-dev \
|
||||
libxrandr-dev \
|
||||
libxinerama-dev \
|
||||
libxcursor-dev \
|
||||
libxi-dev \
|
||||
zlib1g-dev
|
||||
rm -f /usr/lib/python*/EXTERNALLY-MANAGED
|
||||
pip3 install "protobuf<4"
|
||||
- name: Clone main repo if necessary
|
||||
shell: bash
|
||||
run: |
|
||||
if [ ! -d /media/4tbexternal/comaps-init ]; then
|
||||
apt-get update -qq && apt-get install -y --no-install-recommends git
|
||||
cd /media/4tbexternal
|
||||
git clone --recurse-submodules --shallow-submodules -b rebase-generator-pastk-wb251014 --single-branch https://codeberg.org/comaps/comaps.git comaps-init
|
||||
fi
|
||||
- name: Update Isolines
|
||||
shell: bash
|
||||
run: |
|
||||
cd /media/4tbexternal/comaps-init/
|
||||
./tools/unix/build_omim.sh -R topography_generator_tool
|
||||
rm -rf ../osm-planet/isolines/
|
||||
mkdir ../osm-planet/isolines/
|
||||
../omim-build-relwithdebinfo/topography_generator_tool \
|
||||
--profiles_path=./data/conf/isolines/isolines-profiles.json \
|
||||
--countries_to_generate_path=./data/conf/isolines/countries-to-generate.json \
|
||||
--tiles_isolines_out_dir=../osm-planet/isolines/tmp-tiles/ \
|
||||
--countries_isolines_out_dir=../osm-planet/isolines/ \
|
||||
--data_dir=./data/ \
|
||||
--srtm_path=../osm-planet/SRTM-patched-europe/ \
|
||||
--threads=22
|
||||
if [ ! -f /home/planet/planet/planet.o5m ]; then
|
||||
echo "WARN: No file at /home/planet/planet/planet.o5m"
|
||||
|
||||
update-subways:
|
||||
if: inputs.jobs == 'subways' || inputs.jobs == 'all'
|
||||
name: Update Subways
|
||||
runs-on: mapfilemaker
|
||||
container:
|
||||
image: ubuntu:latest
|
||||
volumes:
|
||||
- /media/4tbexternal:/media/4tbexternal
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-map-generator-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
steps:
|
||||
- name: Install dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
apt-get update -qq && apt-get install -y --no-install-recommends curl osmctools osmium-tool python3-venv ca-certificates git python3-pip
|
||||
rm -f /usr/lib/python*/EXTERNALLY-MANAGED
|
||||
pip3 install "protobuf<4"
|
||||
- name: Clone subways if necessary
|
||||
shell: bash
|
||||
run: |
|
||||
if [ ! -d /media/4tbexternal/subways ]; then
|
||||
cd /media/4tbexternal
|
||||
git clone https://codeberg.org/comaps/subways.git
|
||||
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: Clone main repo if necessary
|
||||
shell: bash
|
||||
run: |
|
||||
if [ ! -d /media/4tbexternal/comaps-init ]; then
|
||||
cd /media/4tbexternal
|
||||
git clone --recurse-submodules --shallow-submodules -b rebase-generator-pastk-wb251014 --single-branch https://codeberg.org/comaps/comaps.git comaps-init
|
||||
fi
|
||||
- name: Update Subways
|
||||
shell: bash
|
||||
run: |
|
||||
cd /media/4tbexternal/comaps-init/
|
||||
cp tools/unix/maps/settings.sh.prod tools/unix/maps/settings.sh
|
||||
./tools/unix/maps/generate_subways.sh
|
||||
|
||||
update-tiger:
|
||||
if: inputs.jobs == 'tiger' || inputs.jobs == 'all'
|
||||
name: Update TIGER
|
||||
runs-on: mapfilemaker
|
||||
container:
|
||||
image: ubuntu:latest
|
||||
volumes:
|
||||
- /media/4tbexternal:/media/4tbexternal
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-map-generator-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
steps:
|
||||
- name: Install dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
apt-get update -qq && apt-get install -y --no-install-recommends \
|
||||
build-essential \
|
||||
clang \
|
||||
cmake \
|
||||
ninja-build \
|
||||
ca-certificates \
|
||||
git \
|
||||
wget2
|
||||
- name: Clone main repo if necessary
|
||||
shell: bash
|
||||
run: |
|
||||
if [ ! -d /media/4tbexternal/comaps-init ]; then
|
||||
cd /media/4tbexternal
|
||||
git clone --recurse-submodules --shallow-submodules -b rebase-generator-pastk-wb251014 --single-branch https://codeberg.org/comaps/comaps.git comaps-init
|
||||
echo "Converting planet-latest.osm.pbf to planet.o5m"
|
||||
cd /home/planet/planet/
|
||||
osmconvert -v --drop-author --drop-version --hash-memory=4000 planet-latest.osm.pbf -o=planet.o5m
|
||||
echo "Conversion is done."
|
||||
fi
|
||||
- name: Build address_parser
|
||||
shell: bash
|
||||
- name: Update O5M planet
|
||||
run: |
|
||||
cd /media/4tbexternal/comaps-init
|
||||
rm -rf ../omim-build-relwithdebinfo/CMakeCache.txt
|
||||
rm -rf ../omim-build-relwithdebinfo/CMakeFiles
|
||||
./tools/unix/build_omim.sh -R address_parser_tool
|
||||
- name: Update TIGER from Nominatim
|
||||
shell: bash
|
||||
echo "Starting..."
|
||||
cd /home/planet/planet/
|
||||
rm -f planet-new.o5m
|
||||
osmupdate -v --drop-author --drop-version --hash-memory=4000 --max-merge=32 --out-o5m planet.o5m planet-new.o5m
|
||||
mv planet-new.o5m planet.o5m
|
||||
echo "Done."
|
||||
- name: Notify Zulip
|
||||
run: |
|
||||
cd /media/4tbexternal/osm-planet/
|
||||
wget2 https://nominatim.org/data/tiger-nominatim-preprocessed-latest.csv.tar.gz
|
||||
tar -xOzf tiger-nominatim-preprocessed-latest.csv.tar.gz | /media/4tbexternal/omim-build-relwithdebinfo/address_parser_tool --output_path=./tiger
|
||||
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=O5M planet update is done!'
|
||||
|
||||
generate-maps:
|
||||
if: inputs.jobs == 'maps' || inputs.jobs == 'all'
|
||||
if: inputs.run-mapgen
|
||||
name: Generate Maps
|
||||
runs-on: mapfilemaker
|
||||
needs:
|
||||
- clone-repos
|
||||
timeout-minutes: 40320
|
||||
container:
|
||||
image: ubuntu:latest
|
||||
image: codeberg.org/comaps/maps_generator:f6d53d54f794
|
||||
volumes:
|
||||
- /media/4tbexternal:/media/4tbexternal
|
||||
- /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:
|
||||
- name: Install dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
apt-get update -qq \
|
||||
&& apt-get install -y --no-install-recommends \
|
||||
curl \
|
||||
osmctools \
|
||||
rclone \
|
||||
git \
|
||||
ca-certificates \
|
||||
openssh-client \
|
||||
sshpass \
|
||||
vim \
|
||||
wget \
|
||||
build-essential \
|
||||
clang \
|
||||
cmake \
|
||||
ninja-build \
|
||||
python3 \
|
||||
python3-pip \
|
||||
python3.12-venv \
|
||||
qt6-base-dev \
|
||||
qt6-positioning-dev \
|
||||
libc++-dev \
|
||||
libfreetype-dev \
|
||||
libglvnd-dev \
|
||||
libgl1-mesa-dev \
|
||||
libharfbuzz-dev \
|
||||
libicu-dev \
|
||||
libqt6svg6-dev \
|
||||
libqt6positioning6-plugins \
|
||||
libqt6positioning6 \
|
||||
libsqlite3-dev \
|
||||
libxrandr-dev \
|
||||
libxinerama-dev \
|
||||
libxcursor-dev \
|
||||
libxi-dev \
|
||||
zlib1g-dev
|
||||
- name: Clone repo if necessary
|
||||
shell: bash
|
||||
run: |
|
||||
if [ ! -d /media/4tbexternal/comaps-init ]; then
|
||||
cd /media/4tbexternal
|
||||
git clone --recurse-submodules --shallow-submodules -b rebase-generator-pastk-wb251014 --single-branch https://codeberg.org/comaps/comaps.git comaps-init
|
||||
fi
|
||||
- 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 /media/4tbexternal/osm-maps ]; then
|
||||
mkdir -p /media/4tbexternal/osm-maps
|
||||
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 /media/4tbexternal/osm-planet/SRTM-patched-europe/ ]; then
|
||||
if [ ! -d /home/planet/SRTM-patched-europe/ ]; then
|
||||
echo "ERROR: NO SRTM"
|
||||
exit 1
|
||||
fi
|
||||
- name: Symlink paths for repo scripts
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p /root/OM
|
||||
ln -s /media/4tbexternal/comaps-init /root/OM/organicmaps
|
||||
ln -s /media/4tbexternal/osm-planet /home/planet
|
||||
ln -s /media/4tbexternal/osm-maps /root/OM/maps_build
|
||||
- name: Run docker_maps_generator.sh
|
||||
shell: bash
|
||||
run: |
|
||||
cd /root/OM/organicmaps
|
||||
./tools/unix/docker_maps_generator.sh
|
||||
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.run-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:
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: "~"
|
||||
key: cache-${{ github.run_id }}-${{ github.run_attempt }}
|
||||
- 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,3 +2,5 @@
|
||||
480fa6c2fcf53be296504ac6ba8e6b3d70f92b42
|
||||
a6ede2b1466f0c9d8a443600ef337ba6b5832e58
|
||||
1377b81bf1cac72bb6da192da7fed6696d5d5281
|
||||
0288b97b1367bb971eded1018f560598ea274e6c
|
||||
bf30165b5f5de0907c3c64524a3bf8121624b0b7
|
||||
|
||||
11
.github/workflows/ios-check.yaml
vendored
@@ -8,14 +8,14 @@ on:
|
||||
jobs:
|
||||
ios-check:
|
||||
name: Build iOS
|
||||
runs-on: macos-15
|
||||
runs-on: macos-26
|
||||
env:
|
||||
DEVELOPER_DIR: /Applications/Xcode_26.app/Contents/Developer
|
||||
DEVELOPER_DIR: /Applications/Xcode_26.1.app/Contents/Developer
|
||||
LANG: en_US.UTF-8 # Fastlane complains that the terminal is using ASCII.
|
||||
LANGUAGE: en_US.UTF-8
|
||||
LC_ALL: en_US.UTF-8
|
||||
TEST_RESULTS_BUNDLE_NAME: CoMaps-Test-Results
|
||||
SIMULATOR_DEVICE: 'iPhone 16 Pro Max'
|
||||
SIMULATOR_DEVICE: 'iPhone 17 Pro Max'
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -31,6 +31,9 @@ jobs:
|
||||
brew install qt \
|
||||
optipng
|
||||
pip3 install "protobuf<3.21" --break-system-packages
|
||||
xcodebuild -downloadComponent metalToolchain
|
||||
xcodebuild -downloadPlatform iOS
|
||||
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v4
|
||||
|
||||
@@ -42,7 +45,7 @@ jobs:
|
||||
shell: bash
|
||||
run: ./configure.sh
|
||||
|
||||
- name: Configure XCode cache
|
||||
- name: Configure Xcode cache
|
||||
uses: irgaly/xcode-cache@v1
|
||||
with:
|
||||
key: xcode-cache-deriveddata-${{ github.workflow }}-${{ matrix.buildType }}-${{ github.sha }}
|
||||
|
||||
5
.gitignore
vendored
@@ -9,6 +9,7 @@ Makefile.Release
|
||||
object_script.*.Debug
|
||||
object_script.*.Release
|
||||
compile_commands.json
|
||||
*.local.*
|
||||
|
||||
stxxl.errlog
|
||||
stxxl.log
|
||||
@@ -187,10 +188,6 @@ tools/python/maps_generator/var/etc/map_generator.ini
|
||||
tools/python/routing/etc/*.ini
|
||||
tools/unix/maps/settings.sh
|
||||
|
||||
# Helpers
|
||||
/node_modules/
|
||||
/package-lock.json
|
||||
|
||||
# Visual Studio
|
||||
.vs
|
||||
|
||||
|
||||
83
3party/ankerl/stl.h
Normal file
@@ -0,0 +1,83 @@
|
||||
///////////////////////// ankerl::unordered_dense::{map, set} /////////////////////////
|
||||
|
||||
// A fast & densely stored hashmap and hashset based on robin-hood backward shift deletion.
|
||||
// Version 4.8.1
|
||||
// https://github.com/martinus/unordered_dense
|
||||
//
|
||||
// Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2022 Martin Leitner-Ankerl <martin.ankerl@gmail.com>
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef ANKERL_STL_H
|
||||
#define ANKERL_STL_H
|
||||
|
||||
#include <array> // for array
|
||||
#include <cstdint> // for uint64_t, uint32_t, std::uint8_t, UINT64_C
|
||||
#include <cstring> // for size_t, memcpy, memset
|
||||
#include <functional> // for equal_to, hash
|
||||
#include <initializer_list> // for initializer_list
|
||||
#include <iterator> // for pair, distance
|
||||
#include <limits> // for numeric_limits
|
||||
#include <memory> // for allocator, allocator_traits, shared_ptr
|
||||
#include <optional> // for optional
|
||||
#include <stdexcept> // for out_of_range
|
||||
#include <string> // for basic_string
|
||||
#include <string_view> // for basic_string_view, hash
|
||||
#include <tuple> // for forward_as_tuple
|
||||
#include <type_traits> // for enable_if_t, declval, conditional_t, ena...
|
||||
#include <utility> // for forward, exchange, pair, as_const, piece...
|
||||
#include <vector> // for vector
|
||||
|
||||
// <memory_resource> includes <mutex>, which fails to compile if
|
||||
// targeting GCC >= 13 with the (rewritten) win32 thread model, and
|
||||
// targeting Windows earlier than Vista (0x600). GCC predefines
|
||||
// _REENTRANT when using the 'posix' model, and doesn't when using the
|
||||
// 'win32' model.
|
||||
#if defined __MINGW64__ && defined __GNUC__ && __GNUC__ >= 13 && !defined _REENTRANT
|
||||
// _WIN32_WINNT is guaranteed to be defined here because of the
|
||||
// <cstdint> inclusion above.
|
||||
# ifndef _WIN32_WINNT
|
||||
# error "_WIN32_WINNT not defined"
|
||||
# endif
|
||||
# if _WIN32_WINNT < 0x600
|
||||
# define ANKERL_MEMORY_RESOURCE_IS_BAD() 1 // NOLINT(cppcoreguidelines-macro-usage)
|
||||
# endif
|
||||
#endif
|
||||
#ifndef ANKERL_MEMORY_RESOURCE_IS_BAD
|
||||
# define ANKERL_MEMORY_RESOURCE_IS_BAD() 0 // NOLINT(cppcoreguidelines-macro-usage)
|
||||
#endif
|
||||
|
||||
#if defined(__has_include) && !defined(ANKERL_UNORDERED_DENSE_DISABLE_PMR)
|
||||
# if __has_include(<memory_resource>) && !ANKERL_MEMORY_RESOURCE_IS_BAD()
|
||||
# define ANKERL_UNORDERED_DENSE_PMR std::pmr // NOLINT(cppcoreguidelines-macro-usage)
|
||||
# include <memory_resource> // for polymorphic_allocator
|
||||
# elif __has_include(<experimental/memory_resource>)
|
||||
# define ANKERL_UNORDERED_DENSE_PMR std::experimental::pmr // NOLINT(cppcoreguidelines-macro-usage)
|
||||
# include <experimental/memory_resource> // for polymorphic_allocator
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER) && defined(_M_X64)
|
||||
# include <intrin.h>
|
||||
# pragma intrinsic(_umul128)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
2239
3party/ankerl/unordered_dense.h
Normal file
@@ -114,19 +114,6 @@ if (${CMAKE_BUILD_TYPE} STREQUAL "RelWithDebInfo")
|
||||
add_compile_options(-fno-omit-frame-pointer)
|
||||
endif()
|
||||
|
||||
# Linux GCC LTO plugin fix.
|
||||
if (PLATFORM_LINUX AND (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") AND (CMAKE_BUILD_TYPE MATCHES "^Rel"))
|
||||
# To force errors if LTO was not enabled.
|
||||
add_compile_options(-fno-fat-lto-objects)
|
||||
# To fix ar and ranlib "plugin needed to handle lto object".
|
||||
string(REGEX MATCH "[0-9]+" GCC_MAJOR_VERSION ${CMAKE_CXX_COMPILER_VERSION})
|
||||
file(GLOB_RECURSE plugin /usr/lib/gcc/*/${GCC_MAJOR_VERSION}*/liblto_plugin.so)
|
||||
set(CMAKE_C_ARCHIVE_CREATE "<CMAKE_AR> --plugin ${plugin} qcs <TARGET> <OBJECTS>")
|
||||
set(CMAKE_C_ARCHIVE_FINISH "<CMAKE_RANLIB> --plugin ${plugin} <TARGET>")
|
||||
set(CMAKE_CXX_ARCHIVE_CREATE "<CMAKE_AR> --plugin ${plugin} qcs <TARGET> <OBJECTS>")
|
||||
set(CMAKE_CXX_ARCHIVE_FINISH "<CMAKE_RANLIB> --plugin ${plugin} <TARGET>")
|
||||
endif()
|
||||
|
||||
message(STATUS "Build type: " ${CMAKE_BUILD_TYPE})
|
||||
|
||||
if (PLATFORM_LINUX OR PLATFORM_ANDROID)
|
||||
@@ -222,6 +209,9 @@ if (PLATFORM_DESKTOP AND NOT WITH_SYSTEM_PROVIDED_3PARTY)
|
||||
include_directories("${PROJECT_BINARY_DIR}/3party/gflags/include")
|
||||
endif()
|
||||
|
||||
# Fix for #include <boost/regex.hpp>
|
||||
include_directories("${OMIM_ROOT}/3party/boost")
|
||||
|
||||
# Used in qt/ and shaders/
|
||||
find_package(Python3 REQUIRED COMPONENTS Interpreter)
|
||||
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
This file contains a list of people who have contributed to this project.
|
||||
Its not neccesarily comprehensive.
|
||||
It is not necessarily comprehensive as contributors must manually add themselves.
|
||||
Feel free to add yourself here along with your first contribution!
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
CoMaps contributors:
|
||||
(in alphabetic order)
|
||||
(in alphabetical order)
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
Bastian Greshake Tzovaras
|
||||
clover sage
|
||||
Harry Bond <me@hbond.xyz>
|
||||
thesupertechie
|
||||
vikiawv
|
||||
Yannik Bloscheck
|
||||
|
||||
|
||||
@@ -377,6 +377,7 @@ play {
|
||||
track.set('production')
|
||||
defaultToAppBundles.set(true)
|
||||
releaseStatus.set(ReleaseStatus.IN_PROGRESS)
|
||||
userFraction.set(0.2d) // Rollout to 20% of users
|
||||
serviceAccountCredentials.set(file('google-play.json'))
|
||||
}
|
||||
|
||||
|
||||
32
android/app/src/fdroid/play/listings/ca/full-description.txt
Normal file
@@ -0,0 +1,32 @@
|
||||
Una aplicació de mapes gratuïta i de codi obert dirigida per la comunitat basada en dades d'OpenStreetMap i reforçada amb el compromís amb la transparència, la privadesa i la no ànim de lucre. CoMaps és una derivació/fork d'Organic Maps, que al seu torn és una derivació de Maps.ME.
|
||||
|
||||
Llegiu sobre els motius del projecte i la seva direcció a <b><i>codeberg.org/comaps</i></b>.
|
||||
Uneix-te a la comunitat i ajuda a crear la millor aplicació de mapes
|
||||
• Utilitza l'aplicació i difon-la
|
||||
• Dona comentaris i informa de problemes
|
||||
• Actualitza les dades del mapa a l'aplicació o al lloc web d'OpenStreetMap
|
||||
|
||||
‣ <b>Enfocat fora de línia</b>: Planifica i navega pel teu viatge a l'estranger sense necessitat de servei mòbil, cerca punts de referència mentre fas una excursió llunyana, etc. Totes les funcions de l'aplicació estan dissenyades per funcionar fora de línia.
|
||||
‣ <b>Respecte a la privadesa</b>: L'aplicació està dissenyada tenint en compte la privadesa: no identifica persones, no fa seguiment i no recopila informació personal. Sense anuncis.
|
||||
‣ <b>Senzill i polit</b>: funcions essencials fàcils d'utilitzar que simplement funcionen.
|
||||
‣ <b>Estalvia bateria i espai</b>: No consumeix bateria com altres aplicacions de navegació. Els mapes compactes estalvien espai preciós al telèfon.
|
||||
‣ <b>Gratuït i creat per la comunitat</b>: Gent com tu ha ajudat a crear l'aplicació afegint llocs a OpenStreetMap, provant i donant comentaris sobre les funcions i aportant les seves habilitats de desenvolupament i diners.
|
||||
‣ <b>Presa de decisions i finances obertes i transparents, sense ànim de lucre i de codi obert.</b>
|
||||
|
||||
<b>Característiques principals</b>:
|
||||
• Mapes detallats descarregables amb llocs que no estan disponibles amb Google Maps
|
||||
• Mode exterior amb rutes de senderisme destacades, càmpings, fonts d'aigua, pics, corbes de nivell, etc.
|
||||
• Senders per caminar i carrils bici
|
||||
• Punts d'interès com restaurants, gasolineres, hotels, botigues, llocs d'interès i molts més
|
||||
• Cerca per nom, adreça o categoria de punt d'interès
|
||||
• Navegació amb anuncis de veu per caminar, anar amb bicicleta o conduir
|
||||
• Marca els teus llocs preferits amb un sol toc
|
||||
• Articles de la Viquipèdia fora de línia
|
||||
• Capa i indicacions de trànsit de metro
|
||||
• Enregistrament de rutes
|
||||
• Exporta i importa marcadors i rutes en formats KML, KMZ i GPX
|
||||
• Un mode fosc per utilitzar durant la nit
|
||||
• Millora les dades del mapa per a tothom mitjançant un editor bàsic integrat
|
||||
|
||||
<b>La llibertat és aquí</b>
|
||||
Descobreix el teu viatge, navega pel món amb la privadesa i la comunitat al capdavant!
|
||||
@@ -0,0 +1,33 @@
|
||||
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!
|
||||
@@ -1,8 +1,10 @@
|
||||
• OpenStreetMap-Daten vom 4. November
|
||||
• Aktualisierte Karten-Icons, inkl. Farben für Unterhaltungs-, Sport- & andere Unternehmen
|
||||
• Informationen zu Steckdosen an EV-Ladestationen
|
||||
• Symbole für Sportzentren, Veranstaltungsorte, Massagesalons, Gästehäuser und einige stillgelegte Unternehmen
|
||||
• Verbesserungen bei der Suche
|
||||
• Behebung eines Absturzes bei der Suche
|
||||
• Verbesserte Sprachführung während der Navigation
|
||||
Weitere Änderungen finden in unseren Codeberg-Versionshinweisen!
|
||||
• OpenStreetMap-Daten vom 9. Dezember
|
||||
• Material 3 Design
|
||||
• Im OSM-Editor können nun Ladestationen hinzugefügt werden
|
||||
• Schuko und Typ E Ladestationen hinzugefügt
|
||||
• Verbesserte Suchvorschläge
|
||||
• Litauische und lettische Sprachankündigungen
|
||||
• Die Fahranweisungen wurden vergrößert
|
||||
• Der Zoomlevel passt sich an die Distanz zur nächsten Abbiegung an
|
||||
• Neue Anordnung der Einstellungen
|
||||
Weitere Einzelheiten auf codeberg.org/comaps/comaps/releases
|
||||
|
||||
|
Before Width: | Height: | Size: 628 KiB After Width: | Height: | Size: 636 KiB |
|
Before Width: | Height: | Size: 532 KiB After Width: | Height: | Size: 407 KiB |
|
Before Width: | Height: | Size: 391 KiB After Width: | Height: | Size: 155 KiB |
|
Before Width: | Height: | Size: 1.0 MiB After Width: | Height: | Size: 454 KiB |
|
Before Width: | Height: | Size: 268 KiB After Width: | Height: | Size: 451 KiB |
|
Before Width: | Height: | Size: 263 KiB After Width: | Height: | Size: 357 KiB |
@@ -1,8 +1,10 @@
|
||||
• OpenStreetMap data as of November 4
|
||||
• Recategorized map icons including some new colors for entertainment, sports and other businesses
|
||||
• Display info about available sockets on charging stations
|
||||
• Added bandstands, backless benches and loungers
|
||||
• New icons for different sport centres, event venues, massage salons, guest houses and some disused businesses
|
||||
• Multiple search improvements and crash fix
|
||||
• Improved voice guidance during navigation
|
||||
Check our Codeberg release notes for more changes!
|
||||
• OpenStreetMap data as of December 9
|
||||
• Use Material 3 themes
|
||||
• Support charging sockets in OSM Editor
|
||||
• Added schuko/type-E charge sockets
|
||||
• Improved search results ranking
|
||||
• Enabled Lithuanian and Latvian in voice announcements
|
||||
• Improved size of driving indications
|
||||
• Base zoom level on distance to next turn
|
||||
• Reordered settings
|
||||
More details on codeberg.org/comaps/comaps/releases
|
||||
|
||||
32
android/app/src/fdroid/play/listings/eo/full-description.txt
Normal file
@@ -0,0 +1,32 @@
|
||||
Komunum-gvidata senpaga kaj malfermkoda mapapliko bazita sur OpenStreetMap-datumoj kaj fortigita per komitado al transparencio, privateco kaj ne-lucro. CoMaps estas forko/spin-off de Organic Maps, kiu turne estas forko de Maps.ME.
|
||||
|
||||
Legu plu pri la kialoj por la projekto kaj ĝia direkto ĉe <b><i>codeberg.org/comaps</i></b>.
|
||||
Aliĝu al la komunumo tie kaj helpu fari la plej bonan mapaplikon
|
||||
• Uzu la aplikon kaj disvastigu la vorton pri ĝi
|
||||
• Donu rimarkojn kaj raportu problemojn
|
||||
• Ĝisdatigu mapajn datumojn en la apliko aŭ sur la OpenStreetMap-retejo
|
||||
|
||||
‣ <b>Senkonekta-fokusa</b>: Planu kaj navigu vian vojaĝon eksterlande sen bezono de ĉelulara servico, serĉu vojpunktojn dum malproksima promenado, ktp. Ĉiuj funkcioj de la apliko estas dezajnitaj por funkcii senkonekte.
|
||||
‣ <b>Rispektante Privatecon</b>: La apliko estas dezajnita kun privateco en menso — ne identigas homojn, ne sekvas, kaj ne kolektas personajn informojn. Sen reklamoj.
|
||||
‣ <b>Simpla kaj Perfekta</b>: esencaj, facile uzeblaj funkcioj kiuj simple funkcias.
|
||||
‣ <b>Konservas Vian Baterion kaj Spacon</b>: Ne elĉerpas vian baterion kiel aliaj navigaj aplikaĵoj. Kompaktaj mapoj konservas precian spacon sur via telefono.
|
||||
‣ <b>Sena kaj Konstruita de la Komunumo</b>: Homoj kiel vi helpis konstrui la aplikon per aldonado de lokoj al OpenStreetMap, testado kaj donado de rimarkoj pri funkcioj kaj kontribuado de siaj programadaj kapabloj kaj mono.
|
||||
‣ <b>Malferma kaj Transparenta Decidado kaj Financoj, Ne-lucra kaj Tute Malfermkoda.</b>
|
||||
|
||||
<b>Ĉefaj Funkcioj</b>:
|
||||
• Elŝuteblaj detalegaj mapoj kun lokoj kiuj ne estas disponeblaj per Google Maps
|
||||
• Eksteraj modo kun elstarigitaj promenaj vojoj, kampoj, akvofontoj, pintoj, konturlinioj, ktp
|
||||
• Promenaj vojoj kaj biciklaj vojoj
|
||||
• Interesaj punktoj kiel restoracioj, benzinstacioj, hoteloj, vendejoj, vidindaĵoj kaj multaj aliaj
|
||||
• Serĉo laŭ nomo aŭ adreso aŭ laŭ kategorio de interesaj punktoj
|
||||
• Navigado kun voĉaj anoncoj por promenado, biciklado aŭ veturado
|
||||
• Marku viajn favorajn lokojn per unu tuŝo
|
||||
• Senkonektaj Vikipedio-artikoloj
|
||||
• Metroa transporta tavolo kaj indikoj
|
||||
• Enregistraĵo de vojoj
|
||||
• Eksporto kaj importo de markiloj kaj vojoj en KML, KMZ, GPX formatoj
|
||||
• Malhela modo por uzi nokte
|
||||
• Plibonigu mapajn datumojn por ĉiuj uzante bazan enkonstruitan redaktilon
|
||||
|
||||
<b>Libereco Estas Ĉi Tie</b>
|
||||
Malkovru vian vojaĝon, navigu la mondon kun privateco kaj komunumo en la antaŭa plano!
|
||||
@@ -0,0 +1 @@
|
||||
Facila mapnaviĝado - Malkovru pli de via vojaĝo - Subtenata de la komunumo
|
||||
1
android/app/src/fdroid/play/listings/eo/title.txt
Normal file
@@ -0,0 +1 @@
|
||||
CoMaps - Migru, Biciklu, Veturigu Eksterrete
|
||||
@@ -1,7 +1,11 @@
|
||||
• Datos OSM del 04/11
|
||||
• Iconos del mapa recategorizados, incluyendo nuevos colores
|
||||
• Visualización de información sobre enchufes disponibles en estaciones de recarga
|
||||
• Adición de iconos para diferentes centros deportivos, lugares de eventos, salones de masajes, posadas y algunos establecimientos comerciales desactivados
|
||||
• Varias mejoras y correcciones de errores en la búsqueda
|
||||
• Mejora en la orientación por voz durante la navegación
|
||||
Más detalles en Codeberg
|
||||
• Datos de OpenStreetMap a fecha 9/12.
|
||||
• Uso de temas Material 3.
|
||||
• Compatibilidad con enchufes de recarga en Editor.
|
||||
• Se añaden enchufes de recarga schuko/tipo E.
|
||||
• Se mejora la búsqueda.
|
||||
• Se habilitan el lituano y el letón en las indicaciones de voz.
|
||||
• Se aumenta el tamaño de las indicaciones de conducción.
|
||||
• Nivel de zoom base según la distancia al siguiente giro.
|
||||
• Se han reordenado los ajustes.
|
||||
|
||||
Más detalles en codeberg.org/comaps/comaps/releases
|
||||
|
||||
|
Before Width: | Height: | Size: 655 KiB After Width: | Height: | Size: 605 KiB |
|
Before Width: | Height: | Size: 532 KiB After Width: | Height: | Size: 407 KiB |
|
Before Width: | Height: | Size: 391 KiB After Width: | Height: | Size: 164 KiB |
|
Before Width: | Height: | Size: 1.0 MiB After Width: | Height: | Size: 452 KiB |
|
Before Width: | Height: | Size: 254 KiB After Width: | Height: | Size: 460 KiB |
|
Before Width: | Height: | Size: 263 KiB After Width: | Height: | Size: 355 KiB |
@@ -1,8 +1,10 @@
|
||||
• Données OpenStreetMap au 4 novembre
|
||||
• Recatégorisation des icônes sur la carte avec ajout de nouvelles couleurs pour certains types de lieux
|
||||
• Affichage des prises sur les bornes électriques
|
||||
• Ajout d'icônes pour les centres sportifs, salles d'événements, salon de massage et autres lieux
|
||||
• Multiple améliorations dans la recherche
|
||||
• Correction d'un plantage dans la recherche
|
||||
• Amélioration de la synthèse vocale durant la navigation
|
||||
Plus d'informations sur notre Codeberg
|
||||
• Données OpenStreetMap du 9 Décembre
|
||||
• Utilisation de Material 3
|
||||
• Support de l'édition des bornes de recharge dans l'éditeur OSM
|
||||
• Ajout du type de prise schuko/type-E
|
||||
• Amélioration de l'ordre des résultats de recherche
|
||||
• Ajout du lituanien et du letton dans le guidage vocal
|
||||
• Amélioration de la taille des instructions dans la navigation
|
||||
• Niveau de zoom basé sur la distance jusqu’au prochain virage
|
||||
• Réorganisation des paramètres
|
||||
Plus de détails sur codeberg.org/comaps/comaps/releases
|
||||
|
||||
@@ -1 +1 @@
|
||||
CoMaps - Rando, vélo, conduite hors ligne & privée
|
||||
CoMaps - Randonnée, Vélo, Conduite hors ligne
|
||||
|
||||
10
android/app/src/fdroid/play/listings/hr/release-notes.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
• Podaci OpenStreetMap karte od 9. prosinca
|
||||
• Korištenje Material 3 tema
|
||||
• Podrška za utičnice za punjenje u OSM Editoru
|
||||
• Dodane šuko/tip-E utičnice
|
||||
• Poboljšano rangiranje rezultata pretraživanja
|
||||
• Omogućeni litvanski i latvijski jezici u glasovnim najavama
|
||||
• Povećana veličina indikatora vožnje
|
||||
• Razina zumiranja se mijenja ovisno o udaljenosti do sljedećeg skretanja
|
||||
• Promijenjen redoslijed postavki
|
||||
Više detalja na codeberg.org/comaps/comaps/releases
|
||||
@@ -0,0 +1 @@
|
||||
Paprasta ir patogi navigacija – Turiningos kelionės – Vystoma bendruomenės
|
||||
1
android/app/src/fdroid/play/listings/lt/title.txt
Normal file
@@ -0,0 +1 @@
|
||||
CoMaps – keliaukite atsijungę ir privačiai
|
||||
10
android/app/src/fdroid/play/listings/pl-PL/release-notes.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
• Dane OpenStreetMap z 9 grudnia
|
||||
• Użycie motywów Material 3
|
||||
• Obsługa gniazd ładowania w Edytorze OSM
|
||||
• Dodane gniazda ładowania schuko/type-E
|
||||
• Poprawiony ranking wyników wyszukiwania
|
||||
• Dodane litewskie i łotewskie komunikaty głosowe
|
||||
• Poprawiony rozmiar znaków drogowych
|
||||
• Poziom powiększenia oparty na odległości do następnego manewru
|
||||
• Zmieniona kolejność ustawień
|
||||
Więcej szczegółów na codeberg.org/comaps/comaps/releases
|
||||
@@ -1,7 +1,10 @@
|
||||
• Dados OSM de 04/11
|
||||
• Ícones do mapa recategorizados, incluindo novas cores
|
||||
• Exibição de informações sobre tomadas disponíveis em eletropostos
|
||||
• Adição de ícones para diferentes centros esportivos, locais de eventos, salões de massagem, pousadas e alguns estabelecimentos comerciais desativados
|
||||
• Diversas melhorias e correção de erro na busca
|
||||
• Melhoria na orientação por voz durante a navegação
|
||||
Confira nossas notas de lançamento no Codeberg para mais detalhes!
|
||||
• Dados OpenStreetMap atualizados em 9 de dezembro
|
||||
• Uso do estilo Material 3
|
||||
• Suporte para tomadas de carregamento no Editor OSM
|
||||
• Adição de tomadas de carregamento Schuko/Tipo E
|
||||
• Melhoria na classificação dos resultados de busca
|
||||
• Adição dos idiomas letão e lituano nas orientações por voz
|
||||
• Melhoria no tamanho das indicações de direção
|
||||
• Nível de zoom baseado em distância até a próxima curva
|
||||
• Configurações reordenadas
|
||||
Mais detalhes em codeberg.org/comaps/comaps/releases
|
||||
|
||||
32
android/app/src/fdroid/play/listings/pt/full-description.txt
Normal file
@@ -0,0 +1,32 @@
|
||||
Uma aplicação pela comunidade, grátis e ‘open-source’, de mapas baseada em dados do OpenStreetMap e reforçada com compromisso para transparência, privacidade e sem fins lucrativos. CoMaps é um fork/spin-off de Organic Maps, que, por sua vez, é um fork de Maps.ME
|
||||
|
||||
Leia sobre as razões deste projeto e a sua direção em <b><i>codeberg.org/comaps</i></b>.
|
||||
Junte-se à comunidade e ajude a fazer a melhor aplicação de mapas
|
||||
• Use a aplicação e partilhe-a com outros
|
||||
• Dê ‘feedback’ e reporte problemas
|
||||
• Atualize os dados de mapa na aplicação ou no site do OpenStreetMap
|
||||
|
||||
‣ <b>Simples e Polida</b>: funcionalidades essenciais fáceis que “somente funcionam”.
|
||||
‣ <b>Foco Offline</b>: Planeie e navegue as suas viagens no estrangeiro sem dados móveis, procure locais numa caminhada distante, etc. Todas as funções da aplicação foram criadas com intenção de serem usadas sem internet.
|
||||
‣ <b>Respeita a privacidade</b>: A aplicação foi criada com privacidade em mente — não identifica o utilizador, não rastreia, e não usa a sua informação pessoal. Sem anúncios.
|
||||
‣ <b>Saves Your Battery and Space</b>: Não esgota a sua bateria ao contrário de outras aplicações. Mapas compactos salvam espaço no seu telemóvel.
|
||||
‣ <b>Gratuita e Feita pela Comunidade</b>: Pessoas como si ajudam a criar a aplicação ao adicionar locais ao OpenStreetMap, testando e dando opiniões em funcionalidades e contribuindo com dotes de desenvolvimento e dinheiro.
|
||||
‣ <b>Decisões e Finanças Abertas e Transparentes, Sem fins lucrativos e ‘Open-Source’.</b>
|
||||
|
||||
<b>Funcionalidades principais</b>:
|
||||
• Mapas detalhados descarregáveis com locais que não estão disponíveis com o Google Maps
|
||||
• Modo ao Ar Livre com trilhos de caminhada destacados, acampamentos, fontes de água, cumes, curvas de nível, etc
|
||||
• Caminhos pedestres e ciclovias
|
||||
• Pontos de interesse como restaurantes, estações de serviço, hotéis, lojas, atrações e muitos mais
|
||||
• Pesquise por nome, endereço, ou por categoria de ponto de interesse
|
||||
• Navegação com anúncios de voz ao caminhar, pedalar ou conduzir
|
||||
• Marque os seus locais favoritos com um único clique
|
||||
• Artigos da Wikipédia Offline
|
||||
• Camada de metro e direções
|
||||
• Gravação de Percursos
|
||||
• Exportar e importar marcadores e percursos em formatos KML, KMZ, GPX
|
||||
• Um modo escuro para usar durante a noite
|
||||
• Melhore a informação do mapa para todos com um editor básico embebido
|
||||
|
||||
<b>A liberdade chegou</b>
|
||||
Descubra a sua jornada, navegue o mundo com privacidade e a comunidade à frente!
|
||||
@@ -0,0 +1 @@
|
||||
Navegação fácil nos mapas - Descubra mais sobre o seu percurso - Feito por todos
|
||||
1
android/app/src/fdroid/play/listings/pt/title.txt
Normal file
@@ -0,0 +1 @@
|
||||
CoMaps - Mapas e Navegação - Offline e Privada
|
||||
@@ -1,8 +0,0 @@
|
||||
• Карты OpenStreetMap от 4 ноября
|
||||
• Обновлены цвета иконок на карте, добавлены новые цвета для развлечений, спорта, некоторых бизнесов
|
||||
• На зарядных станциях показываются имеющиеся типы разъёмов
|
||||
• Добавлены эстрады, скамейки без спинок и лежаки
|
||||
• Новые иконки для разных спорт центров, массажных салонов, гостевых домов, некоторых закрытых бизнесов
|
||||
• Несколько улучшений и исправлений в поиске
|
||||
• Улучшены голосовые подсказки при навигации
|
||||
Подробнее смотрите на codeberg.org/comaps/comaps/releases
|
||||
@@ -0,0 +1,32 @@
|
||||
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>
|
||||
உங்கள் பயணத்தைக் கண்டறியவும், தனியுரிமை மற்றும் சமூகத்தை முன்னணியில் கொண்டு உலகிற்கு செல்லவும்!
|
||||
@@ -0,0 +1 @@
|
||||
எளிய வழிகாட்டி - பயணத்தை மேலும் கண்டறி - சமூகத்தால் இயக்கப்படுகிறது
|
||||
1
android/app/src/fdroid/play/listings/ta-IN/title.txt
Normal file
@@ -0,0 +1 @@
|
||||
இணைவரைபடங்கள் - மலையேறு, வண்டி, தனிமையில் இயக்கு
|
||||
@@ -68,4 +68,4 @@ Por favor, informa de errores, sugiere ideas y únete a nuestra comunidad en el
|
||||
|
||||
<b>La Libertad Está Aquí</b>
|
||||
|
||||
Descubre tu camino, navega el mundo con privacidad y con la comunidad como prioridad.
|
||||
¡Descubre tu camino, navega el mundo con privacidad y con la comunidad como prioridad!
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
Paprasta ir patogi navigacija – Turiningos kelionės – Vystoma bendruomenės
|
||||
1
android/app/src/google/play/listings/lt/title.txt
Normal file
@@ -0,0 +1 @@
|
||||
CoMaps – naviguokite privačiai
|
||||
@@ -0,0 +1,36 @@
|
||||
Aplikacja mapowa tworzona przez społeczność, darmowa i open source, oparta na danych OpenStreetMap, z pełnym naciskiem na transparentność, prywatność i działanie non-profit.
|
||||
|
||||
Dołącz do społeczności i pomóż tworzyć najlepszą aplikację mapową
|
||||
• Korzystaj z aplikacji i polecaj ją innym
|
||||
• Przekazuj opinie i zgłaszaj problemy
|
||||
• Aktualizuj dane mapy w aplikacji lub na stronie OpenStreetMap
|
||||
|
||||
<i>Twoje opinie i oceny na 5 gwiazdek są dla nas najlepszym wsparciem!</i>
|
||||
|
||||
‣ <b>Prosta i dopracowana</b>: najważniejsze, łatwe w użyciu funkcje, które po prostu działają.
|
||||
‣ <b>Skoncentrowana na trybie offline</b>: planuj i nawiguj za granicą bez sieci komórkowej, wyszukuj punkty na odległych szlakach – wszystkie funkcje działają offline.
|
||||
‣ <b>Z poszanowaniem prywatności</b>: aplikacja nie identyfikuje użytkowników, nie śledzi i nie zbiera danych osobowych. Bez reklam.
|
||||
‣ <b>Oszczędza baterię i miejsce</b>: nie zużywa baterii jak inne aplikacje nawigacyjne, a kompaktowe mapy oszczędzają miejsce w telefonie.
|
||||
‣ <b>Darmowa i tworzona przez społeczność</b>: ludzie tacy jak Ty dodają miejsca do OpenStreetMap, testują funkcje, zgłaszają opinie i wspierają projekt.
|
||||
‣ <b>Otwarte i przejrzyste decyzje i finanse, działanie non-profit, pełne open source.</b>
|
||||
|
||||
<b>Główne funkcje</b>:
|
||||
• Pobierane szczegółowe mapy z miejscami, których nie ma w Google Maps
|
||||
• Tryb outdoor ze szlakami, biwakami, źródłami wody, szczytami, warstwicami itp.
|
||||
• Ścieżki piesze i rowerowe
|
||||
• Punkty zainteresowania: restauracje, stacje paliw, hotele, sklepy, atrakcje i wiele innych
|
||||
• Wyszukiwanie po nazwie, adresie lub kategorii
|
||||
• Nawigacja z komunikatami głosowymi dla pieszych, rowerzystów i kierowców
|
||||
• Dodawanie ulubionych miejsc jednym tapnięciem
|
||||
• Artykuły Wikipedii dostępne offline
|
||||
• Warstwa metra i wskazówki dojazdu
|
||||
• Nagrywanie śladu trasy
|
||||
• Eksport i import ulubionych miejsc i tras w formatach KML, KMZ, GPX
|
||||
• Tryb ciemny do używania nocą
|
||||
• Poprawianie danych mapy za pomocą prostego wbudowanego edytora
|
||||
• Obsługa Android Auto
|
||||
|
||||
Zgłaszaj problemy, proponuj pomysły i dołącz do społeczności na stronie <b><i>comaps.app</i></b>.
|
||||
|
||||
<b>Wolność jest tutaj</b>
|
||||
Odkrywaj swoją drogę i nawiguj po świecie z prywatnością i społecznością na pierwszym miejscu!
|
||||
@@ -0,0 +1,36 @@
|
||||
திறந்ததெருவரைபடம் தரவை அடிப்படையாகக் கொண்ட சமூகத்தால் வழிநடத்தப்படும் இலவச & திறந்த மூல வரைபடப் பயன்பாடு, வெளிப்படைத்தன்மை, தனியுரிமை மற்றும் இலாப நோக்கற்ற தன்மை ஆகியவற்றிற்கான அர்ப்பணிப்புடன் வலுப்படுத்தப்பட்டுள்ளது.
|
||||
|
||||
சமூகத்தில் சேர்ந்து சிறந்த வரைபடப் பயன்பாட்டை உருவாக்க உதவுங்கள்
|
||||
• பயன்பாட்டைப் பயன்படுத்தி அதைப் பற்றிய செய்தியைப் பரப்புங்கள்
|
||||
• கருத்துத் தெரிவிக்கவும் சிக்கல்களைப் புகாரளிக்கவும்
|
||||
• பயன்பாட்டில் அல்லது OpenStreetMap வலைத்தளத்தில் வரைபடத் தரவைப் புதுப்பிக்கவும்
|
||||
|
||||
<i>உங்கள் கருத்து மற்றும் 5-நட்சத்திர மதிப்புரைகள் எங்களுக்குச் சிறந்த ஆதரவாகும்!</i>
|
||||
|
||||
‣ <b>எளிமையான மற்றும் மெருகூட்டப்பட்ட</b>: அத்தியாவசியமான பயன்படுத்த எளிதான அம்சங்கள் வேலை செய்கின்றன.
|
||||
‣ <b>ஆஃப்லைனில் கவனம் செலுத்துகிறது</b>: செல்லுலார் சேவை தேவை இல்லாமல் உங்கள் வெளிநாட்டு பயணத்தைத் திட்டமிட்டு வழிநடத்துங்கள், தொலைதூர நடைபயணத்தின்போது வழிப்புள்ளிகளைத் தேடுங்கள் போன்றவை. அனைத்து பயன்பாட்டு செயல்பாடுகளும் ஆஃப்லைனில் வேலை செய்ய வடிவமைக்கப்பட்டுள்ளன.
|
||||
‣ <b>தனியுரிமையை மதித்தல்</b>: பயன்பாடு தனியுரிமையை மனதில் கொண்டு வடிவமைக்கப்பட்டுள்ளது - மக்களை அடையாளம் காணாது, கண்காணிக்காது மற்றும் தனிப்பட்ட தகவல்களைச் சேகரிக்காது. விளம்பரங்கள் இல்லாதது.
|
||||
‣ <b>உங்கள் பேட்டரி மற்றும் இடத்தைச் சேமிக்கிறது</b>: பிற வழிசெலுத்தல் பயன்பாடுகளைப் போல உங்கள் பேட்டரியை வெளியேற்றாது. சிறிய வரைபடங்கள் உங்கள் தொலைபேசியில் விலைமதிப்பற்ற இடத்தைச் சேமிக்கின்றன.
|
||||
‣ <b>இலவசம் மற்றும் சமூகத்தால் உருவாக்கப்பட்டது</b>: உங்களைப் போன்றவர்கள் OpenStreetMap இல் இடங்களைச் சேர்ப்பதன் மூலமும், அம்சங்கள்குறித்து சோதித்துப் பார்த்துக் கருத்து தெரிவிப்பதன் மூலமும், அவர்களின் மேம்பாட்டுத் திறன்கள் மற்றும் பணத்தை பங்களிப்பதன் மூலமும் பயன்பாட்டை உருவாக்க உதவினார்கள்.
|
||||
‣ <b>திறந்த மற்றும் வெளிப்படையான முடிவெடுக்கும் மற்றும் நிதி, இலாப நோக்கற்ற மற்றும் முழுமையாகத் திறந்த மூல.</b>
|
||||
|
||||
<b>முக்கிய பண்புகள்</b>:
|
||||
• கூகிள் வரைபடத்தில் கிடைக்காத இடங்களுடன் பதிவிறக்கம் செய்யக்கூடிய விரிவான வரைபடங்கள்
|
||||
• ஹைகிங் பாதைகள், முகாம் தளங்கள், நீர் ஆதாரங்கள், சிகரங்கள், விளிம்புக் கோடுகள் போன்றவற்றுடன் வெளிப்புற பயன்முறை
|
||||
• நடைபாதைகள் மற்றும் சைக்கிள் பாதைகள்
|
||||
• உணவகங்கள், எரிவாயு நிலையங்கள், ஹோட்டல்கள், கடைகள், பார்வையிடல்கள் மற்றும் பல போன்ற ஆர்வமுள்ள இடங்கள்
|
||||
• பெயர் அல்லது முகவரி அல்லது ஆர்வமுள்ள இட வகைமூலம் தேடுங்கள்
|
||||
• நடைபயிற்சி, சைக்கிள் ஓட்டுதல் அல்லது வாகனம் ஓட்டுவதற்கான குரல் அறிவிப்புகளுடன் வழிசெலுத்தல்
|
||||
• உங்களுக்குப் பிடித்த இடங்களை ஒரே தட்டலில் புக்மார்க் செய்யவும்
|
||||
• ஆஃப்லைன் விக்கிபீடியா கட்டுரைகள்
|
||||
• சுரங்கப்பாதை போக்குவரத்து அடுக்கு மற்றும் திசைகள்
|
||||
• பதிவுசெய்தலைக் கண்காணிக்கவும்
|
||||
• KML, KMZ, GPX வடிவங்களில் புக்மார்க்குகள் மற்றும் தடங்களை ஏற்றுமதி செய்து இறக்குமதி செய்யவும்
|
||||
• இரவில் பயன்படுத்த ஒரு இருண்ட பயன்முறை
|
||||
• அடிப்படை உள்ளமைக்கப்பட்ட எடிட்டரைப் பயன்படுத்தி அனைவருக்கும் வரைபடத் தரவை மேம்படுத்தவும்
|
||||
• ஆண்டாய்டு தானி ஆதரவு
|
||||
|
||||
பயன்பாட்டு சிக்கல்களைப் புகாரளிக்கவும், யோசனைகளைப் பரிந்துரைக்கவும் மற்றும் எங்கள் சமூகத்தில் சேரவும் <b><i>comaps.app</i></b> வலைத்தளம்.
|
||||
|
||||
<b>சுதந்திரம் இங்கே உள்ளது</b>
|
||||
உங்கள் பயணத்தைக் கண்டறியவும், தனியுரிமை மற்றும் சமூகத்தை முன்னணியில் வைத்து உலகை வழிநடத்தவும்!
|
||||
@@ -0,0 +1 @@
|
||||
எளிய வழிகாட்டி - பயணத்தை மேலும் கண்டறி - சமூகத்தால் இயக்கப்படுகிறது
|
||||
1
android/app/src/google/play/listings/ta-IN/title.txt
Normal file
@@ -0,0 +1 @@
|
||||
இணைவரைபடங்கள் - தனியுரிமை
|
||||
@@ -28,7 +28,6 @@ import com.github.mikephil.charting.formatter.IAxisValueFormatter;
|
||||
import com.github.mikephil.charting.highlight.Highlight;
|
||||
import com.github.mikephil.charting.listener.OnChartValueSelectedListener;
|
||||
import com.google.android.material.textview.MaterialTextView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
@@ -48,7 +48,6 @@ import androidx.core.content.ContextCompat;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.core.view.WindowInsetsCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentFactory;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentTransaction;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
@@ -108,6 +107,7 @@ import app.organicmaps.sdk.routing.RoutingOptions;
|
||||
import app.organicmaps.sdk.search.SearchEngine;
|
||||
import app.organicmaps.sdk.settings.RoadType;
|
||||
import app.organicmaps.sdk.settings.UnitLocale;
|
||||
import app.organicmaps.sdk.sound.TtsPlayer;
|
||||
import app.organicmaps.sdk.util.Config;
|
||||
import app.organicmaps.sdk.util.LocationUtils;
|
||||
import app.organicmaps.sdk.util.PowerManagment;
|
||||
@@ -133,7 +133,6 @@ import com.google.android.material.appbar.MaterialToolbar;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||
import com.google.android.material.textview.MaterialTextView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Objects;
|
||||
|
||||
@@ -1814,6 +1813,26 @@ public class MwmActivity extends BaseMwmFragmentActivity
|
||||
return false;
|
||||
}
|
||||
|
||||
private void deliverTtsMessage()
|
||||
{
|
||||
if (Config.isTtsMessageDelivered())
|
||||
return;
|
||||
|
||||
String languageDisplayName = TtsPlayer.INSTANCE.getLanguageDisplayName();
|
||||
|
||||
if (languageDisplayName != null)
|
||||
{
|
||||
String navigationStartMessage = getResources().getString(R.string.navigation_start_tts_message);
|
||||
navigationStartMessage += languageDisplayName;
|
||||
Toast.makeText(this, navigationStartMessage, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
else
|
||||
Toast.makeText(this, getResources().getString(R.string.navigation_start_tts_disabled_message), Toast.LENGTH_LONG)
|
||||
.show();
|
||||
|
||||
Config.setTtsMessageDelivered();
|
||||
}
|
||||
|
||||
private boolean showStartPointNotice()
|
||||
{
|
||||
final RoutingController controller = RoutingController.get();
|
||||
@@ -2190,6 +2209,8 @@ public class MwmActivity extends BaseMwmFragmentActivity
|
||||
if (!showRoutingDisclaimer())
|
||||
return;
|
||||
|
||||
deliverTtsMessage();
|
||||
|
||||
closeFloatingPanels();
|
||||
setFullscreen(false);
|
||||
RoutingController.get().start();
|
||||
|
||||
@@ -38,7 +38,8 @@ public class OsmUploadWork extends Worker
|
||||
{
|
||||
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) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
|
||||
{
|
||||
builder.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST);
|
||||
}
|
||||
final OneTimeWorkRequest wr = builder.build();
|
||||
|
||||
@@ -17,7 +17,6 @@ import androidx.annotation.NonNull;
|
||||
import androidx.documentfile.provider.DocumentFile;
|
||||
import app.organicmaps.R;
|
||||
import app.organicmaps.sdk.util.log.Logger;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
|
||||
@@ -10,7 +10,6 @@ import androidx.fragment.app.DialogFragment;
|
||||
|
||||
public class BaseMwmDialogFragment extends DialogFragment
|
||||
{
|
||||
|
||||
protected int getStyle()
|
||||
{
|
||||
return STYLE_NORMAL;
|
||||
|
||||
@@ -18,9 +18,7 @@ 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;
|
||||
|
||||
@@ -42,7 +40,6 @@ 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());
|
||||
|
||||
@@ -282,11 +282,13 @@ 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,
|
||||
R.drawable.ic_search_fail);
|
||||
}
|
||||
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,
|
||||
R.drawable.ic_bookmarks);
|
||||
}
|
||||
|
||||
boolean isEmptyRecycler = isEmpty() || isEmptySearchResults();
|
||||
|
||||
@@ -23,7 +23,6 @@ import app.organicmaps.util.UiUtils;
|
||||
import app.organicmaps.util.Utils;
|
||||
import app.organicmaps.widget.recycler.RecyclerClickListener;
|
||||
import app.organicmaps.widget.recycler.RecyclerLongClickListener;
|
||||
|
||||
import com.google.android.material.button.MaterialButton;
|
||||
import com.google.android.material.checkbox.MaterialCheckBox;
|
||||
import com.google.android.material.imageview.ShapeableImageView;
|
||||
@@ -458,10 +457,12 @@ public class Holders
|
||||
|
||||
String formattedDesc = desc.replace("\n", "<br>");
|
||||
Spanned spannedDesc = Utils.fromHtml(formattedDesc);
|
||||
if (!TextUtils.isEmpty(spannedDesc)) {
|
||||
if (!TextUtils.isEmpty(spannedDesc))
|
||||
{
|
||||
mDescText.setText(spannedDesc);
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
mDescText.setText(R.string.list_description_empty);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,8 +50,8 @@ public final class IntentUtils
|
||||
}
|
||||
|
||||
// https://developer.android.com/reference/androidx/car/app/CarContext#startCarApp(android.content.Intent)
|
||||
private static void processNavigationIntent(@NonNull CarContext carContext,
|
||||
@NonNull Renderer surfaceRenderer, @NonNull Intent intent)
|
||||
private static void processNavigationIntent(@NonNull CarContext carContext, @NonNull Renderer surfaceRenderer,
|
||||
@NonNull Intent intent)
|
||||
{
|
||||
// TODO (AndrewShkrob): This logic will need to be revised when we introduce support for adding stops during
|
||||
// navigation or route planning. Skip navigation intents during navigation
|
||||
|
||||
@@ -85,7 +85,8 @@ public final class RoutingHelpers
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static IconCompat createManeuverIcon(@NonNull final CarContext context, @NonNull CarDirection carDirection, int roundaboutExitNum)
|
||||
private static IconCompat createManeuverIcon(@NonNull final CarContext context, @NonNull CarDirection carDirection,
|
||||
int roundaboutExitNum)
|
||||
{
|
||||
if (!CarDirection.isRoundAbout(carDirection) || roundaboutExitNum == 0)
|
||||
{
|
||||
|
||||
@@ -39,8 +39,7 @@ public final class UiHelpers
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static ActionStrip createMapActionStrip(@NonNull CarContext context,
|
||||
@NonNull Renderer surfaceRenderer)
|
||||
public static ActionStrip createMapActionStrip(@NonNull CarContext context, @NonNull Renderer surfaceRenderer)
|
||||
{
|
||||
final CarIcon iconPlus = new CarIcon.Builder(IconCompat.createWithResource(context, R.drawable.ic_plus)).build();
|
||||
final CarIcon iconMinus = new CarIcon.Builder(IconCompat.createWithResource(context, R.drawable.ic_minus)).build();
|
||||
@@ -59,15 +58,13 @@ public final class UiHelpers
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static MapController createMapController(@NonNull CarContext context,
|
||||
@NonNull Renderer surfaceRenderer)
|
||||
public static MapController createMapController(@NonNull CarContext context, @NonNull Renderer surfaceRenderer)
|
||||
{
|
||||
return new MapController.Builder().setMapActionStrip(createMapActionStrip(context, surfaceRenderer)).build();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static Action createSettingsAction(@NonNull BaseMapScreen mapScreen,
|
||||
@NonNull Renderer surfaceRenderer)
|
||||
public static Action createSettingsAction(@NonNull BaseMapScreen mapScreen, @NonNull Renderer surfaceRenderer)
|
||||
{
|
||||
return createSettingsAction(mapScreen, surfaceRenderer, null);
|
||||
}
|
||||
@@ -81,8 +78,7 @@ public final class UiHelpers
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static Action createSettingsAction(@NonNull BaseMapScreen mapScreen,
|
||||
@NonNull Renderer surfaceRenderer,
|
||||
private static Action createSettingsAction(@NonNull BaseMapScreen mapScreen, @NonNull Renderer surfaceRenderer,
|
||||
@Nullable OnScreenResultListener onScreenResultListener)
|
||||
{
|
||||
final CarContext context = mapScreen.getCarContext();
|
||||
@@ -123,8 +119,7 @@ public final class UiHelpers
|
||||
return null;
|
||||
|
||||
final Row.Builder builder = new Row.Builder();
|
||||
builder.setImage(
|
||||
new CarIcon.Builder(IconCompat.createWithResource(context, R.drawable.ic_opening_hours)).build());
|
||||
builder.setImage(new CarIcon.Builder(IconCompat.createWithResource(context, R.drawable.ic_opening_hours)).build());
|
||||
|
||||
if (isEmptyTT)
|
||||
builder.setTitle(ohStr);
|
||||
@@ -177,7 +172,7 @@ public final class UiHelpers
|
||||
{
|
||||
case LocationState.PENDING_POSITION, LocationState.NOT_FOLLOW_NO_POSITION ->
|
||||
drawableRes = R.drawable.ic_location_off;
|
||||
case LocationState.NOT_FOLLOW -> drawableRes = R.drawable.ic_not_follow;
|
||||
case LocationState.NOT_FOLLOW -> drawableRes = R.drawable.ic_location_crosshair;
|
||||
case LocationState.FOLLOW ->
|
||||
{
|
||||
drawableRes = R.drawable.ic_follow;
|
||||
|
||||
@@ -125,7 +125,8 @@ public class EditTextDialogFragment extends BaseMwmDialogFragment
|
||||
|
||||
positiveButton.setOnClickListener(view -> {
|
||||
final String result = mEtInput.getText().toString();
|
||||
if (validateInput(requireActivity(), result)) {
|
||||
if (validateInput(requireActivity(), result))
|
||||
{
|
||||
processInput(result);
|
||||
editTextDialog.dismiss();
|
||||
}
|
||||
|
||||
@@ -357,7 +357,7 @@ class DownloaderAdapter extends RecyclerView.Adapter<DownloaderAdapter.ViewHolde
|
||||
|
||||
private MenuBottomSheetItem getCancelMenuItem()
|
||||
{
|
||||
return new MenuBottomSheetItem(R.string.cancel, R.drawable.ic_cancel, () -> onCancelActionSelected(mSelectedItem));
|
||||
return new MenuBottomSheetItem(R.string.cancel, R.drawable.ic_close, () -> onCancelActionSelected(mSelectedItem));
|
||||
}
|
||||
|
||||
private class ItemViewHolder extends BaseInnerViewHolder<CountryItem>
|
||||
|
||||
@@ -26,6 +26,10 @@ 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;
|
||||
@@ -33,6 +37,7 @@ 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;
|
||||
|
||||
@@ -44,7 +49,10 @@ public class OnmapDownloader implements MwmActivity.LeftAnimationTrackListener
|
||||
public void onStatusChanged(List<MapManager.StorageCallbackData> data)
|
||||
{
|
||||
if (mCurrentCountry == null)
|
||||
{
|
||||
updateOfflineExplanationVisibility();
|
||||
return;
|
||||
}
|
||||
|
||||
for (MapManager.StorageCallbackData item : data)
|
||||
{
|
||||
@@ -58,7 +66,7 @@ public class OnmapDownloader implements MwmActivity.LeftAnimationTrackListener
|
||||
{
|
||||
mCurrentCountry.update();
|
||||
updateProgressState(false);
|
||||
|
||||
updateOfflineExplanationVisibility();
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -101,6 +109,15 @@ 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);
|
||||
@@ -108,6 +125,8 @@ public class OnmapDownloader implements MwmActivity.LeftAnimationTrackListener
|
||||
|
||||
private void updateStateInternal(boolean shouldAutoDownload)
|
||||
{
|
||||
updateOfflineExplanationVisibility();
|
||||
|
||||
boolean showFrame =
|
||||
(mCurrentCountry != null && !mCurrentCountry.present && !RoutingController.get().isNavigating());
|
||||
if (showFrame)
|
||||
@@ -191,6 +210,9 @@ 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;
|
||||
@@ -247,6 +269,7 @@ public class OnmapDownloader implements MwmActivity.LeftAnimationTrackListener
|
||||
|
||||
public void onResume()
|
||||
{
|
||||
updateOfflineExplanationVisibility();
|
||||
if (mStorageSubscriptionSlot == 0)
|
||||
{
|
||||
mStorageSubscriptionSlot = MapManager.nativeSubscribe(mStorageCallback);
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package app.organicmaps.editor;
|
||||
|
||||
import static android.view.View.INVISIBLE;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
@@ -13,14 +15,12 @@ import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AutoCompleteTextView;
|
||||
import android.widget.GridLayout;
|
||||
import android.widget.Toast;
|
||||
import androidx.annotation.CallSuper;
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.IdRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.appcompat.widget.SwitchCompat;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import app.organicmaps.R;
|
||||
@@ -42,9 +42,9 @@ import app.organicmaps.util.Graphics;
|
||||
import app.organicmaps.util.InputUtils;
|
||||
import app.organicmaps.util.UiUtils;
|
||||
import com.google.android.material.button.MaterialButton;
|
||||
import com.google.android.material.card.MaterialCardView;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
import com.google.android.material.imageview.ShapeableImageView;
|
||||
import com.google.android.material.materialswitch.MaterialSwitch;
|
||||
import com.google.android.material.textfield.TextInputEditText;
|
||||
import com.google.android.material.textfield.TextInputLayout;
|
||||
import com.google.android.material.textview.MaterialTextView;
|
||||
@@ -115,9 +115,9 @@ public class EditorFragment extends BaseMwmFragment implements View.OnClickListe
|
||||
private MaterialTextView mPhone;
|
||||
private MaterialButton mEditPhoneLink;
|
||||
private MaterialTextView mCuisine;
|
||||
private SwitchCompat mWifi;
|
||||
private MaterialSwitch mWifi;
|
||||
private MaterialTextView mSelfService;
|
||||
private SwitchCompat mOutdoorSeating;
|
||||
private MaterialSwitch mOutdoorSeating;
|
||||
|
||||
// Default Metadata entries.
|
||||
private static final class MetadataEntry
|
||||
@@ -153,6 +153,7 @@ 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;
|
||||
|
||||
@@ -352,8 +353,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);
|
||||
@@ -395,24 +395,26 @@ public class EditorFragment extends BaseMwmFragment implements View.OnClickListe
|
||||
typeBtns.removeAllViews();
|
||||
|
||||
List<String> SOCKET_TYPES = Arrays.stream(getResources().getStringArray(R.array.charge_socket_types)).toList();
|
||||
for (String socket : SOCKET_TYPES)
|
||||
for (String socketType : SOCKET_TYPES)
|
||||
{
|
||||
ChargeSocketDescriptor socket = new ChargeSocketDescriptor(socketType, 0, 0);
|
||||
|
||||
MaterialButton btn = (MaterialButton) inflater.inflate(R.layout.button_socket_type, typeBtns, false);
|
||||
|
||||
btn.setTag(R.id.socket_type, socket);
|
||||
btn.setTag(R.id.socket_type, socket.type());
|
||||
|
||||
// load SVG icon converted into VectorDrawable in res/drawable
|
||||
@SuppressLint("DiscouragedApi")
|
||||
int resIconId =
|
||||
getResources().getIdentifier("ic_charge_socket_" + socket, "drawable", requireContext().getPackageName());
|
||||
int resIconId = getResources().getIdentifier("ic_charge_socket_" + socket.visualType(), "drawable",
|
||||
requireContext().getPackageName());
|
||||
if (resIconId != 0)
|
||||
{
|
||||
btn.setIcon(getResources().getDrawable(resIconId));
|
||||
}
|
||||
|
||||
@SuppressLint("DiscouragedApi")
|
||||
int resTypeId =
|
||||
getResources().getIdentifier("charge_socket_" + socket, "string", requireContext().getPackageName());
|
||||
int resTypeId = getResources().getIdentifier("charge_socket_" + socket.visualType(), "string",
|
||||
requireContext().getPackageName());
|
||||
if (resTypeId != 0)
|
||||
{
|
||||
btn.setText(getResources().getString(resTypeId));
|
||||
@@ -460,13 +462,16 @@ public class EditorFragment extends BaseMwmFragment implements View.OnClickListe
|
||||
// Add a TextWatcher to validate on text change
|
||||
countView.addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after)
|
||||
{}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {}
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count)
|
||||
{}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
public void afterTextChanged(Editable s)
|
||||
{
|
||||
validatePositiveField(s.toString(), countInputLayout);
|
||||
}
|
||||
});
|
||||
@@ -481,13 +486,16 @@ public class EditorFragment extends BaseMwmFragment implements View.OnClickListe
|
||||
// Add a TextWatcher to validate on text change
|
||||
powerView.addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after)
|
||||
{}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {}
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count)
|
||||
{}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
public void afterTextChanged(Editable s)
|
||||
{
|
||||
validatePositiveField(s.toString(), powerInputLayout);
|
||||
}
|
||||
});
|
||||
@@ -495,7 +503,8 @@ public class EditorFragment extends BaseMwmFragment implements View.OnClickListe
|
||||
return new MaterialAlertDialogBuilder(requireActivity(), R.style.MwmTheme_AlertDialog)
|
||||
.setTitle(R.string.editor_socket)
|
||||
.setView(dialogView)
|
||||
.setPositiveButton(R.string.save,
|
||||
.setPositiveButton(
|
||||
R.string.save,
|
||||
(dialog, which) -> {
|
||||
String socketType = "";
|
||||
for (MaterialButton b : buttonList)
|
||||
@@ -539,30 +548,37 @@ public class EditorFragment extends BaseMwmFragment implements View.OnClickListe
|
||||
Logger.w(CHARGE_SOCKETS_TAG, "Invalid power value for socket:" + powerView.getText().toString());
|
||||
}
|
||||
|
||||
ChargeSocketDescriptor socket =
|
||||
new ChargeSocketDescriptor(socketType, countValue, powerValue);
|
||||
ChargeSocketDescriptor socket = new ChargeSocketDescriptor(socketType, countValue, powerValue);
|
||||
|
||||
updateChargeSockets(socketIndex, socket);
|
||||
})
|
||||
.setNegativeButton(R.string.cancel, (dialog, which) -> { dialog.dismiss(); });
|
||||
.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss());
|
||||
}
|
||||
|
||||
// Helper method for validation logic
|
||||
private boolean validatePositiveField(String text, TextInputLayout layout) {
|
||||
if (text.isEmpty()) {
|
||||
private boolean validatePositiveField(String text, TextInputLayout layout)
|
||||
{
|
||||
if (text.isEmpty())
|
||||
{
|
||||
layout.setError(null); // No error if empty (assuming 0 is the default)
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
try
|
||||
{
|
||||
double value = Double.parseDouble(text);
|
||||
if (value < 0) {
|
||||
if (value < 0)
|
||||
{
|
||||
layout.setError(getString(R.string.error_value_must_be_positive));
|
||||
return false;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
layout.setError(null);
|
||||
return true;
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
}
|
||||
catch (NumberFormatException e)
|
||||
{
|
||||
layout.setError(getString(R.string.error_invalid_number));
|
||||
return false;
|
||||
}
|
||||
@@ -582,7 +598,8 @@ public class EditorFragment extends BaseMwmFragment implements View.OnClickListe
|
||||
{
|
||||
sockets[socketIndex] = socket;
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
List<ChargeSocketDescriptor> list = new ArrayList<>(Arrays.asList(sockets));
|
||||
list.add(socket);
|
||||
sockets = list.toArray(new ChargeSocketDescriptor[0]);
|
||||
@@ -614,7 +631,7 @@ public class EditorFragment extends BaseMwmFragment implements View.OnClickListe
|
||||
|
||||
// load SVG icon converted into VectorDrawable in res/drawable
|
||||
@SuppressLint("DiscouragedApi")
|
||||
int resIconId = getResources().getIdentifier("ic_charge_socket_" + socket.type(), "drawable",
|
||||
int resIconId = getResources().getIdentifier("ic_charge_socket_" + socket.visualType(), "drawable",
|
||||
requireContext().getPackageName());
|
||||
if (resIconId != 0)
|
||||
{
|
||||
@@ -622,8 +639,8 @@ public class EditorFragment extends BaseMwmFragment implements View.OnClickListe
|
||||
}
|
||||
|
||||
@SuppressLint("DiscouragedApi")
|
||||
int resTypeId =
|
||||
getResources().getIdentifier("charge_socket_" + socket.type(), "string", requireContext().getPackageName());
|
||||
int resTypeId = getResources().getIdentifier("charge_socket_" + socket.visualType(), "string",
|
||||
requireContext().getPackageName());
|
||||
if (resTypeId != 0)
|
||||
{
|
||||
type.setText(resTypeId);
|
||||
@@ -634,23 +651,24 @@ public class EditorFragment extends BaseMwmFragment implements View.OnClickListe
|
||||
DecimalFormat df = new DecimalFormat("#.##");
|
||||
power.setText(getString(R.string.kw_label, df.format(socket.power())));
|
||||
}
|
||||
else if (socket.ignorePower())
|
||||
{
|
||||
power.setVisibility(INVISIBLE);
|
||||
}
|
||||
|
||||
if (socket.count() != 0)
|
||||
{
|
||||
count.setText(getString(R.string.count_label, socket.count()));
|
||||
}
|
||||
|
||||
itemView.setOnClickListener(v -> {
|
||||
buildChargeSocketDialog(currentIndex, socket.type(), socket.count(), socket.power()).show();
|
||||
});
|
||||
itemView.setOnClickListener(
|
||||
v -> buildChargeSocketDialog(currentIndex, socket.type(), socket.count(), socket.power()).show());
|
||||
socketsGrid.addView(itemView);
|
||||
}
|
||||
|
||||
// add a 'new item' button at the end, to create new sockets
|
||||
View btnNewItemView = inflater.inflate(R.layout.button_new_item, socketsGrid, false);
|
||||
btnNewItemView.setOnClickListener(v -> {
|
||||
buildChargeSocketDialog(-1, "unknown", -1, -1).show();
|
||||
});
|
||||
btnNewItemView.setOnClickListener(v -> buildChargeSocketDialog(-1, "unknown", -1, -1).show());
|
||||
socketsGrid.addView(btnNewItemView);
|
||||
}
|
||||
|
||||
@@ -790,9 +808,8 @@ public class EditorFragment extends BaseMwmFragment implements View.OnClickListe
|
||||
View lineContactBlock =
|
||||
initBlock(view, Metadata.MetadataType.FMD_CONTACT_LINE, R.id.block_line, R.drawable.ic_line_white,
|
||||
R.string.editor_line_social_network, InputType.TYPE_TEXT_VARIATION_URI);
|
||||
View blueskyContactBlock =
|
||||
initBlock(view, Metadata.MetadataType.FMD_CONTACT_BLUESKY, R.id.block_bluesky, R.drawable.ic_bluesky,
|
||||
R.string.bluesky, InputType.TYPE_TEXT_VARIATION_URI);
|
||||
View blueskyContactBlock = initBlock(view, Metadata.MetadataType.FMD_CONTACT_BLUESKY, R.id.block_bluesky,
|
||||
R.drawable.ic_bluesky, R.string.bluesky, InputType.TYPE_TEXT_VARIATION_URI);
|
||||
View operatorBlock = initBlock(view, Metadata.MetadataType.FMD_OPERATOR, R.id.block_operator,
|
||||
R.drawable.ic_operator, R.string.editor_operator, 0);
|
||||
|
||||
@@ -827,6 +844,8 @@ 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);
|
||||
@@ -894,6 +913,8 @@ 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();
|
||||
}
|
||||
@@ -939,9 +960,12 @@ 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);
|
||||
@@ -1014,6 +1038,20 @@ 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);
|
||||
|
||||
@@ -358,7 +358,7 @@ public class EditorHostFragment
|
||||
.show();
|
||||
}
|
||||
|
||||
private void processEditedFeatures()
|
||||
public void processEditedFeatures()
|
||||
{
|
||||
if (OsmOAuth.isAuthorized())
|
||||
{
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
package app.organicmaps.editor;
|
||||
|
||||
import android.content.res.ColorStateList;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import app.organicmaps.R;
|
||||
import app.organicmaps.sdk.editor.data.FeatureCategory;
|
||||
import app.organicmaps.sdk.util.StringUtils;
|
||||
import app.organicmaps.util.UiUtils;
|
||||
import com.google.android.material.button.MaterialButton;
|
||||
import com.google.android.material.textfield.TextInputEditText;
|
||||
import com.google.android.material.textview.MaterialTextView;
|
||||
|
||||
@@ -65,8 +68,7 @@ public class FeatureCategoryAdapter extends RecyclerView.Adapter<RecyclerView.Vi
|
||||
}
|
||||
case TYPE_FOOTER ->
|
||||
{
|
||||
return new FooterViewHolder(inflater.inflate(R.layout.item_feature_category_footer, parent, false),
|
||||
mFragment);
|
||||
return new FooterViewHolder(inflater.inflate(R.layout.item_feature_category_footer, parent, false), mFragment);
|
||||
}
|
||||
default -> throw new IllegalArgumentException("Unsupported viewType: " + viewType);
|
||||
}
|
||||
@@ -119,7 +121,7 @@ public class FeatureCategoryAdapter extends RecyclerView.Adapter<RecyclerView.Vi
|
||||
protected static class FooterViewHolder extends RecyclerView.ViewHolder
|
||||
{
|
||||
private final TextInputEditText mNoteEditText;
|
||||
private final View mSendNoteButton;
|
||||
private final MaterialButton mSendNoteButton;
|
||||
|
||||
FooterViewHolder(@NonNull View itemView, @NonNull FooterListener listener)
|
||||
{
|
||||
@@ -129,6 +131,24 @@ public class FeatureCategoryAdapter extends RecyclerView.Adapter<RecyclerView.Vi
|
||||
mNoteEditText = itemView.findViewById(R.id.note_edit_text);
|
||||
mSendNoteButton = itemView.findViewById(R.id.send_note_button);
|
||||
mSendNoteButton.setOnClickListener(v -> listener.onSendNoteClicked());
|
||||
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(mSendNoteButton.getContext(), R.color.base_accent),
|
||||
ContextCompat.getColor(mSendNoteButton.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(mSendNoteButton.getContext(),
|
||||
UiUtils.getStyledResourceId(mSendNoteButton.getContext(),
|
||||
android.R.attr.textColorPrimaryInverse)),
|
||||
ContextCompat.getColor(mSendNoteButton.getContext(), R.color.button_accent_text_disabled)});
|
||||
mSendNoteButton.setBackgroundTintList(bgButtonColor);
|
||||
mSendNoteButton.setTextColor(textButtonColor);
|
||||
mNoteEditText.addTextChangedListener(new StringUtils.SimpleTextWatcher() {
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count)
|
||||
|
||||
@@ -2,19 +2,16 @@ 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;
|
||||
import com.google.android.material.timepicker.MaterialTimePicker;
|
||||
import com.google.android.material.timepicker.TimeFormat;
|
||||
|
||||
public class FromToTimePicker
|
||||
{
|
||||
@@ -32,18 +29,11 @@ public class FromToTimePicker
|
||||
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,
|
||||
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);
|
||||
FromToTimePicker timePicker = new FromToTimePicker(fragment, listener, fromTime, toTime, id);
|
||||
|
||||
if (startWithToTime)
|
||||
timePicker.showToTimePicker();
|
||||
@@ -51,11 +41,8 @@ public class FromToTimePicker
|
||||
timePicker.showFromTimePicker();
|
||||
}
|
||||
|
||||
private FromToTimePicker(@NonNull Fragment fragment,
|
||||
@NonNull FromToTimePicker.OnPickListener listener,
|
||||
@NonNull HoursMinutes fromTime,
|
||||
@NonNull HoursMinutes toTime,
|
||||
int id)
|
||||
private FromToTimePicker(@NonNull Fragment fragment, @NonNull FromToTimePicker.OnPickListener listener,
|
||||
@NonNull HoursMinutes fromTime, @NonNull HoursMinutes toTime, int id)
|
||||
{
|
||||
mActivity = fragment.requireActivity();
|
||||
mFragmentManager = fragment.getChildFragmentManager();
|
||||
@@ -100,15 +87,12 @@ public class FromToTimePicker
|
||||
|
||||
private MaterialTimePicker buildFromTimePicker()
|
||||
{
|
||||
MaterialTimePicker timePicker = buildTimePicker(mFromTime,
|
||||
mResources.getString(R.string.editor_time_from),
|
||||
mResources.getString(R.string.next_button),
|
||||
null);
|
||||
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 ->
|
||||
{
|
||||
timePicker.addOnPositiveButtonClickListener(view -> {
|
||||
mIsFromTimePicked = true;
|
||||
saveState(timePicker, true);
|
||||
mFromTimePicker = null;
|
||||
@@ -122,13 +106,10 @@ public class FromToTimePicker
|
||||
|
||||
private MaterialTimePicker buildToTimePicker()
|
||||
{
|
||||
MaterialTimePicker timePicker = buildTimePicker(mToTime,
|
||||
mResources.getString(R.string.editor_time_to),
|
||||
null,
|
||||
MaterialTimePicker timePicker = buildTimePicker(mToTime, mResources.getString(R.string.editor_time_to), null,
|
||||
mResources.getString(R.string.back));
|
||||
|
||||
timePicker.addOnNegativeButtonClickListener(view ->
|
||||
{
|
||||
timePicker.addOnNegativeButtonClickListener(view -> {
|
||||
saveState(timePicker, false);
|
||||
mToTimePicker = null;
|
||||
if (mIsFromTimePicked)
|
||||
@@ -137,8 +118,7 @@ public class FromToTimePicker
|
||||
finishTimePicking(false);
|
||||
});
|
||||
|
||||
timePicker.addOnPositiveButtonClickListener(view ->
|
||||
{
|
||||
timePicker.addOnPositiveButtonClickListener(view -> {
|
||||
saveState(timePicker, false);
|
||||
finishTimePicking(true);
|
||||
});
|
||||
@@ -149,16 +129,16 @@ public class FromToTimePicker
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private MaterialTimePicker buildTimePicker(@NonNull HoursMinutes time,
|
||||
@NonNull String title,
|
||||
private MaterialTimePicker buildTimePicker(@NonNull HoursMinutes time, @NonNull String title,
|
||||
@Nullable String positiveButtonTextOverride,
|
||||
@Nullable String negativeButtonTextOverride)
|
||||
{
|
||||
MaterialTimePicker.Builder builder = new MaterialTimePicker.Builder()
|
||||
MaterialTimePicker.Builder builder =
|
||||
new MaterialTimePicker.Builder()
|
||||
.setTitleText(title)
|
||||
.setTimeFormat(mIs24HourFormat ? TimeFormat.CLOCK_24H : TimeFormat.CLOCK_12H)
|
||||
.setInputMode(mInputMode)
|
||||
.setTheme(R.style.MwmMain_MaterialTimePicker)
|
||||
.setTheme(R.style.MwmTheme_MaterialTimePicker)
|
||||
.setHour((int) time.hours)
|
||||
.setMinute((int) time.minutes);
|
||||
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
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;
|
||||
@@ -11,6 +14,8 @@ import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
public class LanguagesFragment extends BaseMwmRecyclerFragment<LanguagesAdapter>
|
||||
@@ -32,10 +37,28 @@ 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<>(systemLocales.size());
|
||||
for (int i = 0; i < systemLocales.size(); i++)
|
||||
systemLanguages.add(null);
|
||||
|
||||
for (Language lang : Editor.nativeGetSupportedLanguages(false))
|
||||
{
|
||||
if (existingLanguages.contains(lang.code))
|
||||
// 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(i, lang);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (existingLanguages.contains(lang.code) || systemLanguages.contains(lang))
|
||||
continue;
|
||||
|
||||
languages.add(lang);
|
||||
@@ -43,6 +66,8 @@ public class LanguagesFragment extends BaseMwmRecyclerFragment<LanguagesAdapter>
|
||||
|
||||
Collections.sort(languages, Comparator.comparing(lhs -> lhs.name));
|
||||
|
||||
languages.addAll(0, systemLanguages.stream().filter(Objects::nonNull).toList());
|
||||
|
||||
return new LanguagesAdapter(this, languages.toArray(new Language[languages.size()]));
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ProgressBar;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.view.ViewCompat;
|
||||
@@ -23,6 +22,7 @@ import app.organicmaps.util.Utils;
|
||||
import app.organicmaps.util.WindowInsetUtils;
|
||||
import app.organicmaps.widget.StackedButtonDialogFragment;
|
||||
import com.google.android.material.imageview.ShapeableImageView;
|
||||
import com.google.android.material.progressindicator.CircularProgressIndicator;
|
||||
import com.google.android.material.textview.MaterialTextView;
|
||||
import java.text.NumberFormat;
|
||||
|
||||
@@ -50,7 +50,7 @@ public class ProfileFragment extends BaseMwmToolbarFragment
|
||||
private MaterialTextView mEditsSent;
|
||||
private MaterialTextView mProfileName;
|
||||
private ShapeableImageView mProfileImage;
|
||||
private ProgressBar mProfileInfoLoading;
|
||||
private CircularProgressIndicator mProfileInfoLoading;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
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;
|
||||
@@ -23,6 +23,7 @@ import app.organicmaps.sdk.util.Utils;
|
||||
import app.organicmaps.util.UiUtils;
|
||||
import com.google.android.material.button.MaterialButton;
|
||||
import com.google.android.material.checkbox.MaterialCheckBox;
|
||||
import com.google.android.material.materialswitch.MaterialSwitch;
|
||||
import com.google.android.material.textview.MaterialTextView;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@@ -120,20 +121,14 @@ class SimpleTimetableAdapter extends RecyclerView.Adapter<SimpleTimetableAdapter
|
||||
notifyItemChanged(getItemCount() - 1);
|
||||
}
|
||||
|
||||
private void pickTime(int position,
|
||||
@IntRange(from = ID_OPENING_TIME, to = ID_CLOSED_SPAN) int id,
|
||||
private void pickTime(int position, @IntRange(from = ID_OPENING_TIME, to = ID_CLOSED_SPAN) int id,
|
||||
boolean startWithToTime)
|
||||
{
|
||||
final Timetable data = mItems.get(position);
|
||||
mPickingPosition = position;
|
||||
|
||||
FromToTimePicker.pickTime(mFragment,
|
||||
this,
|
||||
data.workingTimespan.start,
|
||||
data.workingTimespan.end,
|
||||
id,
|
||||
FromToTimePicker.pickTime(mFragment, this, data.workingTimespan.start, data.workingTimespan.end, id,
|
||||
startWithToTime);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -193,7 +188,7 @@ class SimpleTimetableAdapter extends RecyclerView.Adapter<SimpleTimetableAdapter
|
||||
|
||||
SparseArray<MaterialCheckBox> days = new SparseArray<>(7);
|
||||
View allday;
|
||||
SwitchCompat swAllday;
|
||||
MaterialSwitch swAllday;
|
||||
View schedule;
|
||||
View openClose;
|
||||
View open;
|
||||
@@ -381,6 +376,24 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,8 +9,8 @@ import androidx.annotation.Nullable;
|
||||
import app.organicmaps.R;
|
||||
import app.organicmaps.base.BaseMwmRecyclerFragment;
|
||||
|
||||
public class SimpleTimetableFragment extends BaseMwmRecyclerFragment<SimpleTimetableAdapter>
|
||||
implements TimetableProvider
|
||||
public class SimpleTimetableFragment
|
||||
extends BaseMwmRecyclerFragment<SimpleTimetableAdapter> implements TimetableProvider
|
||||
{
|
||||
private SimpleTimetableAdapter mAdapter;
|
||||
@Nullable
|
||||
|
||||
@@ -2,14 +2,12 @@ package app.organicmaps.maplayer;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
import androidx.annotation.AttrRes;
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.StringRes;
|
||||
import app.organicmaps.R;
|
||||
import app.organicmaps.adapter.OnItemClickListener;
|
||||
import app.organicmaps.sdk.maplayer.Mode;
|
||||
import app.organicmaps.util.ThemeUtils;
|
||||
|
||||
public class LayerBottomSheetItem
|
||||
{
|
||||
@@ -37,26 +35,29 @@ public class LayerBottomSheetItem
|
||||
@DrawableRes
|
||||
int drawableResId = 0;
|
||||
@StringRes
|
||||
int buttonTextResource = R.string.layers_title;
|
||||
switch (mode)
|
||||
int buttonTextResource = switch (mode)
|
||||
{
|
||||
case OUTDOORS ->
|
||||
{
|
||||
case OUTDOORS:
|
||||
drawableResId = R.drawable.ic_layers_outdoors;
|
||||
buttonTextResource = R.string.button_layer_outdoor;
|
||||
break;
|
||||
case SUBWAY:
|
||||
drawableResId = R.drawable.ic_layers_subway;
|
||||
buttonTextResource = R.string.subway;
|
||||
break;
|
||||
case ISOLINES:
|
||||
drawableResId = R.drawable.ic_layers_isoline;
|
||||
buttonTextResource = R.string.button_layer_isolines;
|
||||
break;
|
||||
case TRAFFIC:
|
||||
drawableResId = R.drawable.ic_layers_traffic;
|
||||
buttonTextResource = R.string.button_layer_traffic;
|
||||
break;
|
||||
yield R.string.button_layer_outdoor;
|
||||
}
|
||||
case SUBWAY ->
|
||||
{
|
||||
drawableResId = R.drawable.ic_layers_subway;
|
||||
yield R.string.subway;
|
||||
}
|
||||
case ISOLINES ->
|
||||
{
|
||||
drawableResId = R.drawable.ic_layers_isoline;
|
||||
yield R.string.button_layer_isolines;
|
||||
}
|
||||
case TRAFFIC ->
|
||||
{
|
||||
drawableResId = R.drawable.ic_layers_traffic;
|
||||
yield R.string.button_layer_traffic;
|
||||
}
|
||||
};
|
||||
return new LayerBottomSheetItem(drawableResId, buttonTextResource, mode, layerItemClickListener);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,11 +5,9 @@ import android.widget.ImageView;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.google.android.material.textview.MaterialTextView;
|
||||
|
||||
import app.organicmaps.R;
|
||||
import app.organicmaps.adapter.OnItemClickListener;
|
||||
import com.google.android.material.textview.MaterialTextView;
|
||||
|
||||
class LayerHolder extends RecyclerView.ViewHolder
|
||||
{
|
||||
|
||||
@@ -43,7 +43,6 @@ import app.organicmaps.util.UiUtils;
|
||||
import app.organicmaps.util.Utils;
|
||||
import app.organicmaps.widget.recycler.DotDividerItemDecoration;
|
||||
import app.organicmaps.widget.recycler.MultilineLayoutManager;
|
||||
|
||||
import com.google.android.material.button.MaterialButton;
|
||||
import com.google.android.material.imageview.ShapeableImageView;
|
||||
import com.google.android.material.textview.MaterialTextView;
|
||||
@@ -123,9 +122,9 @@ final class RoutingBottomMenuController implements View.OnClickListener
|
||||
@NonNull View timeElevationLine, @NonNull View transitFrame,
|
||||
@NonNull MaterialTextView error, @NonNull MaterialButton start,
|
||||
@NonNull ShapeableImageView altitudeChart, @NonNull MaterialTextView time,
|
||||
@NonNull MaterialTextView altitudeDifference, @NonNull MaterialTextView timeVehicle,
|
||||
@Nullable MaterialTextView arrival, @NonNull View actionFrame,
|
||||
@Nullable RoutingBottomMenuListener listener)
|
||||
@NonNull MaterialTextView altitudeDifference,
|
||||
@NonNull MaterialTextView timeVehicle, @Nullable MaterialTextView arrival,
|
||||
@NonNull View actionFrame, @Nullable RoutingBottomMenuListener listener)
|
||||
{
|
||||
mContext = context;
|
||||
mAltitudeChartFrame = altitudeChartFrame;
|
||||
|
||||
@@ -12,9 +12,6 @@ import androidx.annotation.IdRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.core.view.WindowInsetsCompat;
|
||||
|
||||
import com.google.android.material.textview.MaterialTextView;
|
||||
|
||||
import app.organicmaps.MwmApplication;
|
||||
import app.organicmaps.R;
|
||||
import app.organicmaps.sdk.Framework;
|
||||
@@ -29,6 +26,7 @@ import app.organicmaps.util.WindowInsetUtils.PaddingInsetsListener;
|
||||
import app.organicmaps.widget.RoutingToolbarButton;
|
||||
import app.organicmaps.widget.ToolbarController;
|
||||
import app.organicmaps.widget.WheelProgressView;
|
||||
import com.google.android.material.textview.MaterialTextView;
|
||||
|
||||
public class RoutingPlanController extends ToolbarController
|
||||
{
|
||||
|
||||
@@ -14,12 +14,10 @@ import androidx.annotation.NonNull;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.google.android.material.textview.MaterialTextView;
|
||||
|
||||
import app.organicmaps.R;
|
||||
import app.organicmaps.sdk.search.DisplayedCategories;
|
||||
import app.organicmaps.sdk.util.Language;
|
||||
import com.google.android.material.textview.MaterialTextView;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.Locale;
|
||||
|
||||
@@ -5,10 +5,8 @@ import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.StyleRes;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import app.organicmaps.base.BaseMwmFragmentActivity;
|
||||
import app.organicmaps.util.ThemeUtils;
|
||||
|
||||
public class SearchActivity extends BaseMwmFragmentActivity
|
||||
{
|
||||
|
||||
@@ -10,17 +10,18 @@ import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.google.android.material.textview.MaterialTextView;
|
||||
|
||||
import app.organicmaps.R;
|
||||
import app.organicmaps.sdk.search.SearchResult;
|
||||
import app.organicmaps.util.Graphics;
|
||||
import app.organicmaps.util.ThemeUtils;
|
||||
import app.organicmaps.util.UiUtils;
|
||||
import com.google.android.material.textview.MaterialTextView;
|
||||
|
||||
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;
|
||||
@@ -149,41 +150,36 @@ class SearchAdapter extends RecyclerView.Adapter<SearchAdapter.SearchDataViewHol
|
||||
{
|
||||
final Resources resources = mSearchFragment.getResources();
|
||||
|
||||
switch (result.description.openNow)
|
||||
if (result.description.openNow != SearchResult.OPEN_NOW_YES
|
||||
&& result.description.openNow != SearchResult.OPEN_NOW_NO)
|
||||
{
|
||||
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);
|
||||
// Hide if unknown opening hours state
|
||||
UiUtils.hide(mOpen);
|
||||
return;
|
||||
}
|
||||
|
||||
UiUtils.setTextAndShow(mOpen, string);
|
||||
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)
|
||||
{
|
||||
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);
|
||||
|
||||
UiUtils.setTextAndShow(mOpen, nextChangeFormatted);
|
||||
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));
|
||||
}
|
||||
}
|
||||
case SearchResult.OPEN_NOW_NO ->
|
||||
{
|
||||
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, 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));
|
||||
}
|
||||
}
|
||||
default -> UiUtils.hide(mOpen);
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -273,7 +273,8 @@ 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,
|
||||
R.drawable.ic_search_fail);
|
||||
mSearchAdapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver()
|
||||
|
||||
{
|
||||
|
||||
@@ -5,15 +5,13 @@ import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.google.android.material.textview.MaterialTextView;
|
||||
|
||||
import app.organicmaps.MwmApplication;
|
||||
import app.organicmaps.R;
|
||||
import app.organicmaps.sdk.routing.RoutingController;
|
||||
import app.organicmaps.sdk.search.SearchRecents;
|
||||
import app.organicmaps.util.Graphics;
|
||||
import app.organicmaps.widget.SearchToolbarController;
|
||||
import com.google.android.material.textview.MaterialTextView;
|
||||
|
||||
class SearchHistoryAdapter extends RecyclerView.Adapter<SearchHistoryAdapter.ViewHolder>
|
||||
{
|
||||
|
||||
@@ -8,12 +8,12 @@ import android.view.ViewGroup;
|
||||
import android.widget.CompoundButton;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.widget.SwitchCompat;
|
||||
import app.organicmaps.R;
|
||||
import app.organicmaps.base.BaseMwmToolbarFragment;
|
||||
import app.organicmaps.sdk.routing.RoutingController;
|
||||
import app.organicmaps.sdk.routing.RoutingOptions;
|
||||
import app.organicmaps.sdk.settings.RoadType;
|
||||
import com.google.android.material.materialswitch.MaterialSwitch;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
@@ -88,34 +88,34 @@ public class DrivingOptionsFragment extends BaseMwmToolbarFragment
|
||||
|
||||
private void initViews(@NonNull View root)
|
||||
{
|
||||
SwitchCompat tollsBtn = root.findViewById(R.id.avoid_tolls_btn);
|
||||
MaterialSwitch tollsBtn = root.findViewById(R.id.avoid_tolls_btn);
|
||||
tollsBtn.setChecked(RoutingOptions.hasOption(RoadType.Toll));
|
||||
CompoundButton.OnCheckedChangeListener tollBtnListener = new ToggleRoutingOptionListener(RoadType.Toll, root);
|
||||
tollsBtn.setOnCheckedChangeListener(tollBtnListener);
|
||||
|
||||
SwitchCompat motorwaysBtn = root.findViewById(R.id.avoid_motorways_btn);
|
||||
MaterialSwitch motorwaysBtn = root.findViewById(R.id.avoid_motorways_btn);
|
||||
motorwaysBtn.setChecked(RoutingOptions.hasOption(RoadType.Motorway));
|
||||
CompoundButton.OnCheckedChangeListener motorwayBtnListener =
|
||||
new ToggleRoutingOptionListener(RoadType.Motorway, root);
|
||||
motorwaysBtn.setOnCheckedChangeListener(motorwayBtnListener);
|
||||
|
||||
SwitchCompat ferriesBtn = root.findViewById(R.id.avoid_ferries_btn);
|
||||
MaterialSwitch ferriesBtn = root.findViewById(R.id.avoid_ferries_btn);
|
||||
ferriesBtn.setChecked(RoutingOptions.hasOption(RoadType.Ferry));
|
||||
CompoundButton.OnCheckedChangeListener ferryBtnListener = new ToggleRoutingOptionListener(RoadType.Ferry, root);
|
||||
ferriesBtn.setOnCheckedChangeListener(ferryBtnListener);
|
||||
|
||||
SwitchCompat dirtyRoadsBtn = root.findViewById(R.id.avoid_dirty_roads_btn);
|
||||
MaterialSwitch 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);
|
||||
dirtyRoadsBtn.setOnCheckedChangeListener(dirtyBtnListener);
|
||||
|
||||
SwitchCompat stepsBtn = root.findViewById(R.id.avoid_steps_btn);
|
||||
MaterialSwitch stepsBtn = root.findViewById(R.id.avoid_steps_btn);
|
||||
stepsBtn.setChecked(RoutingOptions.hasOption(RoadType.Steps));
|
||||
CompoundButton.OnCheckedChangeListener stepsBtnListener = new ToggleRoutingOptionListener(RoadType.Steps, root);
|
||||
stepsBtn.setOnCheckedChangeListener(stepsBtnListener);
|
||||
|
||||
SwitchCompat pavedBtn = root.findViewById(R.id.avoid_paved_roads_btn);
|
||||
MaterialSwitch 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);
|
||||
@@ -144,8 +144,8 @@ public class DrivingOptionsFragment extends BaseMwmToolbarFragment
|
||||
else
|
||||
RoutingOptions.removeOption(mRoadType);
|
||||
|
||||
SwitchCompat dirtyRoadsBtn = mRoot.findViewById(R.id.avoid_dirty_roads_btn);
|
||||
SwitchCompat pavedBtn = mRoot.findViewById(R.id.avoid_paved_roads_btn);
|
||||
MaterialSwitch dirtyRoadsBtn = mRoot.findViewById(R.id.avoid_dirty_roads_btn);
|
||||
MaterialSwitch pavedBtn = mRoot.findViewById(R.id.avoid_paved_roads_btn);
|
||||
if (mRoadType == RoadType.Dirty)
|
||||
{
|
||||
pavedBtn.setEnabled(!isChecked);
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -90,7 +90,6 @@ public enum ThemeSwitcher
|
||||
{
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
|
||||
uiModeManager.setApplicationNightMode(UiModeManager.MODE_NIGHT_YES);
|
||||
else
|
||||
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
|
||||
|
||||
if (RoutingController.get().isVehicleNavigation())
|
||||
@@ -104,7 +103,6 @@ public enum ThemeSwitcher
|
||||
{
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
|
||||
uiModeManager.setApplicationNightMode(UiModeManager.MODE_NIGHT_NO);
|
||||
else
|
||||
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
|
||||
|
||||
if (RoutingController.get().isVehicleNavigation())
|
||||
|
||||
@@ -8,19 +8,20 @@ import android.graphics.Typeface;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
public abstract class BaseSignView extends View
|
||||
{
|
||||
private float mBorderWidthRatio = 0.1f;
|
||||
protected void setBorderWidthRatio(float ratio) {
|
||||
protected void setBorderWidthRatio(float ratio)
|
||||
{
|
||||
mBorderWidthRatio = ratio;
|
||||
}
|
||||
|
||||
private float mBorderInsetRatio = 0f;
|
||||
protected void setBorderInsetRatio(float ratio) {
|
||||
protected void setBorderInsetRatio(float ratio)
|
||||
{
|
||||
mBorderInsetRatio = ratio;
|
||||
}
|
||||
|
||||
@@ -54,11 +55,7 @@ public abstract class BaseSignView extends View
|
||||
mTextPaint.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.BOLD));
|
||||
}
|
||||
|
||||
protected void setColors(int backgroundColor,
|
||||
int borderColor,
|
||||
int alertColor,
|
||||
int textColor,
|
||||
int textAlertColor)
|
||||
protected void setColors(int backgroundColor, int borderColor, int alertColor, int textColor, int textAlertColor)
|
||||
{
|
||||
mBackgroundColor = backgroundColor;
|
||||
mBorderColor = borderColor;
|
||||
@@ -72,7 +69,8 @@ public abstract class BaseSignView extends View
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) {
|
||||
protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight)
|
||||
{
|
||||
super.onSizeChanged(width, height, oldWidth, oldHeight);
|
||||
final float paddingX = getPaddingLeft() + getPaddingRight();
|
||||
final float paddingY = getPaddingTop() + getPaddingBottom();
|
||||
@@ -91,7 +89,8 @@ public abstract class BaseSignView extends View
|
||||
{
|
||||
super.onDraw(canvas);
|
||||
final String str = getValueString();
|
||||
if (str == null) return;
|
||||
if (str == null)
|
||||
return;
|
||||
|
||||
final float cx = mWidth / 2f;
|
||||
final float cy = mHeight / 2f;
|
||||
@@ -143,7 +142,8 @@ public abstract class BaseSignView extends View
|
||||
void configureTextSize()
|
||||
{
|
||||
String text = getValueString();
|
||||
if (text == null) return;
|
||||
if (text == null)
|
||||
return;
|
||||
final float textRadius = mBorderRadius - mBorderWidth;
|
||||
final float maxTextSize = 2f * textRadius;
|
||||
final float maxTextSize2 = maxTextSize * maxTextSize;
|
||||
|
||||
@@ -4,9 +4,7 @@ import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Pair;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import app.organicmaps.R;
|
||||
import app.organicmaps.sdk.util.StringUtils;
|
||||
|
||||
@@ -22,8 +20,8 @@ public class CurrentSpeedView extends BaseSignView
|
||||
setBorderWidthRatio(0.1f);
|
||||
setBorderInsetRatio(0.05f);
|
||||
|
||||
try (TypedArray a = ctx.getTheme()
|
||||
.obtainStyledAttributes(attrs, R.styleable.CurrentSpeedView /* reuse same attrs or define new */ , 0, 0))
|
||||
try (TypedArray a = ctx.getTheme().obtainStyledAttributes(
|
||||
attrs, R.styleable.CurrentSpeedView /* reuse same attrs or define new */, 0, 0))
|
||||
{
|
||||
int bg = a.getColor(R.styleable.CurrentSpeedView_currentSpeedBackgroundColor, DefaultValues.BACKGROUND_COLOR);
|
||||
int bd = a.getColor(R.styleable.CurrentSpeedView_currentSpeedBorderColor, DefaultValues.BORDER_COLOR);
|
||||
|
||||
@@ -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;
|
||||
@@ -13,12 +12,10 @@ 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;
|
||||
|
||||
import app.organicmaps.R;
|
||||
import app.organicmaps.util.UiUtils;
|
||||
import com.google.android.material.imageview.ShapeableImageView;
|
||||
import com.google.android.material.textview.MaterialTextView;
|
||||
|
||||
public class PlaceholderView extends LinearLayout
|
||||
{
|
||||
|
||||
@@ -7,7 +7,6 @@ import androidx.annotation.DrawableRes;
|
||||
import androidx.appcompat.content.res.AppCompatResources;
|
||||
import androidx.appcompat.widget.AppCompatRadioButton;
|
||||
import app.organicmaps.R;
|
||||
import app.organicmaps.util.ThemeUtils;
|
||||
|
||||
public class RoutingToolbarButton extends AppCompatRadioButton
|
||||
{
|
||||
|
||||
@@ -5,9 +5,7 @@ import android.content.res.TypedArray;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import app.organicmaps.R;
|
||||
|
||||
public class SpeedLimitView extends BaseSignView
|
||||
@@ -27,15 +25,22 @@ public class SpeedLimitView extends BaseSignView
|
||||
|
||||
try (TypedArray styleAttrs = ctx.getTheme().obtainStyledAttributes(attrs, R.styleable.SpeedLimitView, 0, 0))
|
||||
{
|
||||
final int bgColor = styleAttrs.getColor(R.styleable.SpeedLimitView_speedLimitBackgroundColor, DefaultValues.BACKGROUND_COLOR);
|
||||
final int borderColor = styleAttrs.getColor(R.styleable.SpeedLimitView_speedLimitBorderColor, DefaultValues.BORDER_COLOR);
|
||||
final int alertColor = styleAttrs.getColor(R.styleable.SpeedLimitView_speedLimitAlertColor, DefaultValues.ALERT_COLOR);
|
||||
final int textColor = styleAttrs.getColor(R.styleable.SpeedLimitView_speedLimitTextColor, DefaultValues.TEXT_COLOR);
|
||||
final int txtAlertColor = styleAttrs.getColor(R.styleable.SpeedLimitView_speedLimitTextAlertColor, DefaultValues.TEXT_ALERT_COLOR);
|
||||
final int bgColor =
|
||||
styleAttrs.getColor(R.styleable.SpeedLimitView_speedLimitBackgroundColor, DefaultValues.BACKGROUND_COLOR);
|
||||
final int borderColor =
|
||||
styleAttrs.getColor(R.styleable.SpeedLimitView_speedLimitBorderColor, DefaultValues.BORDER_COLOR);
|
||||
final int alertColor =
|
||||
styleAttrs.getColor(R.styleable.SpeedLimitView_speedLimitAlertColor, DefaultValues.ALERT_COLOR);
|
||||
final int textColor =
|
||||
styleAttrs.getColor(R.styleable.SpeedLimitView_speedLimitTextColor, DefaultValues.TEXT_COLOR);
|
||||
final int txtAlertColor =
|
||||
styleAttrs.getColor(R.styleable.SpeedLimitView_speedLimitTextAlertColor, DefaultValues.TEXT_ALERT_COLOR);
|
||||
setColors(bgColor, borderColor, alertColor, textColor, txtAlertColor);
|
||||
|
||||
unlimitedBorderColor = styleAttrs.getColor(R.styleable.SpeedLimitView_speedLimitUnlimitedBorderColor, DefaultValues.UNLIMITED_BORDER_COLOR);
|
||||
unlimitedStripeColor = styleAttrs.getColor(R.styleable.SpeedLimitView_speedLimitUnlimitedStripeColor, DefaultValues.UNLIMITED_STRIPE_COLOR);
|
||||
unlimitedBorderColor = styleAttrs.getColor(R.styleable.SpeedLimitView_speedLimitUnlimitedBorderColor,
|
||||
DefaultValues.UNLIMITED_BORDER_COLOR);
|
||||
unlimitedStripeColor = styleAttrs.getColor(R.styleable.SpeedLimitView_speedLimitUnlimitedStripeColor,
|
||||
DefaultValues.UNLIMITED_STRIPE_COLOR);
|
||||
|
||||
if (isInEditMode())
|
||||
{
|
||||
@@ -122,7 +127,6 @@ public class SpeedLimitView extends BaseSignView
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private interface DefaultValues
|
||||
{
|
||||
int BACKGROUND_COLOR = 0xFFFFFFFF;
|
||||
|
||||
@@ -67,7 +67,7 @@ public class MyPositionButton
|
||||
{
|
||||
case LocationState.PENDING_POSITION -> R.drawable.ic_menu_location_pending;
|
||||
case LocationState.NOT_FOLLOW_NO_POSITION -> R.drawable.ic_location_off;
|
||||
case LocationState.NOT_FOLLOW -> R.drawable.ic_not_follow;
|
||||
case LocationState.NOT_FOLLOW -> R.drawable.ic_location_crosshair;
|
||||
case LocationState.FOLLOW -> R.drawable.ic_follow;
|
||||
case LocationState.FOLLOW_AND_ROTATE -> R.drawable.ic_follow_and_rotate;
|
||||
default -> throw new IllegalArgumentException("Invalid button mode: " + mode);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package app.organicmaps.widget.placepage;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import app.organicmaps.sdk.util.StringUtils;
|
||||
import com.github.mikephil.charting.charts.BarLineChartBase;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.github.mikephil.charting.components.AxisBase;
|
||||
import com.github.mikephil.charting.formatter.IAxisValueFormatter;
|
||||
|
||||
|
||||
@@ -105,7 +105,8 @@ public class EditBookmarkFragment extends BaseMwmDialogFragment implements View.
|
||||
public EditBookmarkFragment() {}
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
public void onCreate(@Nullable Bundle savedInstanceState)
|
||||
{
|
||||
super.onCreate(savedInstanceState);
|
||||
setStyle(DialogFragment.STYLE_NORMAL, R.style.MwmTheme_FullScreenDialog);
|
||||
}
|
||||
@@ -184,10 +185,9 @@ public class EditBookmarkFragment extends BaseMwmDialogFragment implements View.
|
||||
{
|
||||
super.onStart();
|
||||
Dialog dialog = getDialog();
|
||||
if (dialog != null) {
|
||||
dialog.getWindow().setLayout(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.MATCH_PARENT);
|
||||
if (dialog != null)
|
||||
{
|
||||
dialog.getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
|
||||
}
|
||||
|
||||
// Focus name and show keyboard for "Unknown Place" bookmarks
|
||||
|
||||
@@ -6,9 +6,6 @@ import android.widget.RelativeLayout;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.widget.NestedScrollView;
|
||||
|
||||
import com.google.android.material.textview.MaterialTextView;
|
||||
|
||||
import app.organicmaps.ChartController;
|
||||
import app.organicmaps.R;
|
||||
import app.organicmaps.sdk.Framework;
|
||||
@@ -17,6 +14,7 @@ import app.organicmaps.sdk.bookmarks.data.Track;
|
||||
import app.organicmaps.sdk.bookmarks.data.TrackStatistics;
|
||||
import app.organicmaps.util.UiUtils;
|
||||
import app.organicmaps.util.Utils;
|
||||
import com.google.android.material.textview.MaterialTextView;
|
||||
import java.util.Objects;
|
||||
|
||||
public class ElevationProfileViewRenderer implements PlacePageStateListener
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -55,7 +55,6 @@ import app.organicmaps.sdk.downloader.MapManager;
|
||||
import app.organicmaps.sdk.editor.Editor;
|
||||
import app.organicmaps.sdk.editor.OhState;
|
||||
import app.organicmaps.sdk.editor.OpeningHours;
|
||||
import app.organicmaps.sdk.editor.data.HoursMinutes;
|
||||
import app.organicmaps.sdk.editor.data.Timetable;
|
||||
import app.organicmaps.sdk.location.LocationListener;
|
||||
import app.organicmaps.sdk.location.SensorListener;
|
||||
@@ -85,9 +84,11 @@ 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>,
|
||||
@@ -105,6 +106,9 @@ 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);
|
||||
@@ -149,6 +153,7 @@ public class PlacePageView extends Fragment
|
||||
private MaterialTextView mTvLastChecked;
|
||||
private View mEditPlace;
|
||||
private View mAddPlace;
|
||||
private View mMapTooOld;
|
||||
private View mEditTopSpace;
|
||||
private ShapeableImageView mColorIcon;
|
||||
private MaterialTextView mTvCategory;
|
||||
@@ -313,6 +318,7 @@ public class PlacePageView extends Fragment
|
||||
mTvLastChecked = mFrame.findViewById(R.id.place_page_last_checked);
|
||||
mEditPlace = mFrame.findViewById(R.id.ll__place_editor);
|
||||
mAddPlace = mFrame.findViewById(R.id.ll__place_add);
|
||||
mMapTooOld = mFrame.findViewById(R.id.cv__map_too_old);
|
||||
mEditTopSpace = mFrame.findViewById(R.id.edit_top_space);
|
||||
latlon.setOnLongClickListener(this);
|
||||
address.setOnLongClickListener(this);
|
||||
@@ -679,42 +685,72 @@ public class PlacePageView extends Fragment
|
||||
|
||||
if (RoutingController.get().isNavigating() || RoutingController.get().isPlanning())
|
||||
{
|
||||
UiUtils.hide(mEditPlace, mAddPlace, mEditTopSpace);
|
||||
UiUtils.hide(mEditPlace, mAddPlace, mEditTopSpace, mMapTooOld);
|
||||
}
|
||||
else
|
||||
{
|
||||
UiUtils.showIf(Editor.nativeShouldShowEditPlace(), mEditPlace);
|
||||
UiUtils.showIf(Editor.nativeShouldShowAddPlace(), mAddPlace);
|
||||
UiUtils.hide(mMapTooOld);
|
||||
MaterialButton mTvEditPlace = mEditPlace.findViewById(R.id.mb__place_editor);
|
||||
MaterialButton mTvAddPlace = mAddPlace.findViewById(R.id.mb__place_add);
|
||||
|
||||
boolean shouldEnableEditPlace = Editor.nativeShouldEnableEditPlace();
|
||||
|
||||
if (shouldEnableEditPlace)
|
||||
{
|
||||
mTvEditPlace.setOnClickListener(this);
|
||||
mTvAddPlace.setOnClickListener(this);
|
||||
mTvEditPlace.setEnabled(Editor.nativeShouldEnableEditPlace());
|
||||
mTvAddPlace.setEnabled(Editor.nativeShouldEnableAddPlace());
|
||||
final int editTextButtonColor =
|
||||
Editor.nativeShouldEnableEditPlace()
|
||||
}
|
||||
else
|
||||
{
|
||||
mTvEditPlace.setOnClickListener(
|
||||
(v) -> { Utils.showSnackbar(v.getContext(), v.getRootView(), R.string.place_page_too_old_to_edit); });
|
||||
mTvAddPlace.setOnClickListener(
|
||||
(v) -> { Utils.showSnackbar(v.getContext(), v.getRootView(), R.string.place_page_too_old_to_edit); });
|
||||
|
||||
String countryId = MapManager.nativeGetSelectedCountry();
|
||||
|
||||
if (countryId != null)
|
||||
{
|
||||
CountryItem map = CountryItem.fill(countryId);
|
||||
|
||||
if (map.status == CountryItem.STATUS_UPDATABLE || map.status == CountryItem.STATUS_DONE
|
||||
|| map.status == CountryItem.STATUS_FAILED)
|
||||
{
|
||||
UiUtils.show(mMapTooOld);
|
||||
|
||||
boolean canUpdateMap = map.status != CountryItem.STATUS_DONE;
|
||||
MaterialButton mTvUpdateTooOldMap = mMapTooOld.findViewById(R.id.mb__update_too_old_map);
|
||||
UiUtils.showIf(canUpdateMap, mTvUpdateTooOldMap);
|
||||
|
||||
MaterialTextView mapTooOldDescription = mMapTooOld.findViewById(R.id.tv__map_too_old_description);
|
||||
if (canUpdateMap)
|
||||
{
|
||||
mapTooOldDescription.setText(R.string.place_page_map_too_old_description);
|
||||
mTvUpdateTooOldMap.setOnClickListener((v) -> {
|
||||
MapManagerHelper.warn3gAndDownload(requireActivity(), map.id, null);
|
||||
UiUtils.hide(mMapTooOld);
|
||||
});
|
||||
}
|
||||
else
|
||||
mapTooOldDescription.setText(R.string.place_page_app_too_old_description);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final int editButtonColor =
|
||||
shouldEnableEditPlace
|
||||
? 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);
|
||||
UiUtils.showIf(
|
||||
UiUtils.isVisible(mEditPlace) || UiUtils.isVisible(mAddPlace),
|
||||
mEditTopSpace);
|
||||
|
||||
mTvEditPlace.setTextColor(editButtonColor);
|
||||
mTvAddPlace.setTextColor(editButtonColor);
|
||||
mTvEditPlace.setStrokeColor(ColorStateList.valueOf(editButtonColor));
|
||||
mTvAddPlace.setStrokeColor(ColorStateList.valueOf(editButtonColor));
|
||||
UiUtils.showIf(UiUtils.isVisible(mEditPlace) || UiUtils.isVisible(mAddPlace), mEditTopSpace);
|
||||
}
|
||||
updateLinksView();
|
||||
updateOpeningHoursView();
|
||||
@@ -797,8 +833,13 @@ public class PlacePageView extends Fragment
|
||||
final String ohStr = mMapObject.getMetadata(Metadata.MetadataType.FMD_OPEN_HOURS);
|
||||
final Timetable[] timetables = OpeningHours.nativeTimetablesFromString(ohStr);
|
||||
|
||||
if (timetables != null && timetables.length != 0)
|
||||
// No valid timetable
|
||||
if (timetables == null || timetables.length == 0)
|
||||
{
|
||||
UiUtils.hide(mTvOpenState);
|
||||
return;
|
||||
}
|
||||
|
||||
final Context context = requireContext();
|
||||
final OhState poiState = OpeningHours.nativeCurrentState(timetables);
|
||||
|
||||
@@ -810,8 +851,7 @@ public class PlacePageView extends Fragment
|
||||
}
|
||||
|
||||
// Get colours
|
||||
final ForegroundColorSpan colorGreen =
|
||||
new ForegroundColorSpan(ContextCompat.getColor(context, R.color.base_green));
|
||||
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));
|
||||
@@ -820,34 +860,95 @@ public class PlacePageView extends Fragment
|
||||
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
|
||||
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)
|
||||
{
|
||||
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));
|
||||
try
|
||||
{
|
||||
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;
|
||||
}
|
||||
else if (isOpen)
|
||||
}
|
||||
catch (Throwable ignored)
|
||||
{}
|
||||
}
|
||||
|
||||
if (!hasFiniteNextChange) // No valid next change
|
||||
{
|
||||
if (isOpen)
|
||||
openStateString.append(getString(R.string.open_now), colorGreen, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
// TODO: Add "Closes at 18:00" etc
|
||||
else // Closed
|
||||
else
|
||||
openStateString.append(getString(R.string.closed_now), colorRed, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
// TODO: Add "Opens at 18:00" etc
|
||||
|
||||
UiUtils.setTextAndHideIfEmpty(mTvOpenState, openStateString);
|
||||
return;
|
||||
}
|
||||
// No valid timetable
|
||||
UiUtils.hide(mTvOpenState);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
private void addPlace()
|
||||
|
||||
@@ -3,9 +3,7 @@ package app.organicmaps.widget.placepage;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
import androidx.lifecycle.ViewModel;
|
||||
|
||||
import app.organicmaps.sdk.bookmarks.data.MapObject;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class PlacePageViewModel extends ViewModel
|
||||
|
||||
@@ -31,7 +31,8 @@ import app.organicmaps.widget.placepage.PlacePageViewModel;
|
||||
import com.google.android.material.textview.MaterialTextView;
|
||||
|
||||
public class PlacePageBookmarkFragment extends Fragment implements View.OnClickListener, View.OnLongClickListener,
|
||||
Observer<MapObject>, EditBookmarkFragment.EditBookmarkListener
|
||||
Observer<MapObject>,
|
||||
EditBookmarkFragment.EditBookmarkListener
|
||||
{
|
||||
private View mFrame;
|
||||
private MaterialTextView mTvBookmarkNote;
|
||||
|
||||
@@ -1,28 +1,25 @@
|
||||
package app.organicmaps.widget.placepage.sections;
|
||||
|
||||
import static android.view.View.INVISIBLE;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.GridLayout;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.lifecycle.Observer;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
|
||||
import com.google.android.material.imageview.ShapeableImageView;
|
||||
import com.google.android.material.textview.MaterialTextView;
|
||||
|
||||
import app.organicmaps.R;
|
||||
import app.organicmaps.sdk.Framework;
|
||||
import app.organicmaps.sdk.bookmarks.data.ChargeSocketDescriptor;
|
||||
import app.organicmaps.sdk.bookmarks.data.MapObject;
|
||||
import app.organicmaps.sdk.bookmarks.data.Metadata;
|
||||
import app.organicmaps.widget.placepage.PlacePageViewModel;
|
||||
import com.google.android.material.imageview.ShapeableImageView;
|
||||
import com.google.android.material.textview.MaterialTextView;
|
||||
import java.text.DecimalFormat;
|
||||
|
||||
public class PlacePageChargeSocketsFragment extends Fragment implements Observer<MapObject>
|
||||
@@ -89,7 +86,7 @@ public class PlacePageChargeSocketsFragment extends Fragment implements Observer
|
||||
|
||||
// load SVG icon converted into VectorDrawable in res/drawable
|
||||
@SuppressLint("DiscouragedApi")
|
||||
int resIconId = getResources().getIdentifier("ic_charge_socket_" + socket.type(), "drawable",
|
||||
int resIconId = getResources().getIdentifier("ic_charge_socket_" + socket.visualType(), "drawable",
|
||||
requireContext().getPackageName());
|
||||
if (resIconId != 0)
|
||||
{
|
||||
@@ -97,8 +94,8 @@ public class PlacePageChargeSocketsFragment extends Fragment implements Observer
|
||||
}
|
||||
|
||||
@SuppressLint("DiscouragedApi")
|
||||
int resTypeId =
|
||||
getResources().getIdentifier("charge_socket_" + socket.type(), "string", requireContext().getPackageName());
|
||||
int resTypeId = getResources().getIdentifier("charge_socket_" + socket.visualType(), "string",
|
||||
requireContext().getPackageName());
|
||||
if (resTypeId != 0)
|
||||
{
|
||||
type.setText(resTypeId);
|
||||
@@ -109,6 +106,10 @@ public class PlacePageChargeSocketsFragment extends Fragment implements Observer
|
||||
DecimalFormat df = new DecimalFormat("#.##");
|
||||
power.setText(getString(R.string.kw_label, df.format(socket.power())));
|
||||
}
|
||||
else if (socket.ignorePower())
|
||||
{
|
||||
power.setVisibility(INVISIBLE);
|
||||
}
|
||||
|
||||
if (socket.count() != 0)
|
||||
{
|
||||
|
||||
@@ -192,7 +192,8 @@ public class PlacePageLinksFragment extends Fragment implements Observer<MapObje
|
||||
default -> mMapObject.getMetadata(type);
|
||||
};
|
||||
// Add user names for social media if available
|
||||
if (!TextUtils.isEmpty(title) && !title.equals(url) && !title.contains("/")) items.add(title);
|
||||
if (!TextUtils.isEmpty(title) && !title.equals(url) && !title.contains("/"))
|
||||
items.add(title);
|
||||
|
||||
if (items.size() == 1)
|
||||
PlacePageUtils.copyToClipboard(requireContext(), mFrame, items.get(0));
|
||||
|
||||
20
android/app/src/main/res/color/m3_dark_highlighted_text.xml
Normal file
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2021 The Android Open Source Project
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
<!-- Material3 alternative to textColorHighlight for dark theme -->
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:alpha="@dimen/material_emphasis_medium" android:color="@color/m3_sys_color_dark_primary" />
|
||||
</selector>
|
||||