Merge commit '07cd1ec4f5' into traffic

# Conflicts:
#	libs/indexer/ftypes_matcher.hpp

Bug: boost/regex.hpp may not be found
Workaround: remove `if` in CMakeLists.txt:226, leave `include_directories`
This commit is contained in:
mvglasow
2025-11-23 17:04:45 +02:00
684 changed files with 15498 additions and 4773 deletions

View File

@@ -5,10 +5,10 @@ on:
jobs:
description: 'Which job(s) to run right now?'
required: true
default: 'all'
default: 'all-except-upload'
type: choice
options:
- all
- all-except-upload
- copy-coasts
- planet
- wiki
@@ -16,30 +16,82 @@ on:
- subways
- tiger
- maps
- upload
map-generator-continue:
description: 'Continue previous map generation?'
required: false
default: false
type: boolean
map-generator-countries:
description: 'Generate specific MWMs? (i.e. "US_New York_*, foo")'
required: false
type: string
reset:
description: 'Reset part of the system?'
required: false
default: 'no'
type: choice
options:
- 'no'
- wiki-ratelimit
## RCLONE_CONF is multi-line text containing keys and credentials for us2,ru1,fi1,de1 servers
env:
RCLONE_CONF: ${{ secrets.RCLONE_CONF }}
WIKIMEDIA_USERNAME: ${{ secrets.WIKIMEDIA_USERNAME }}
WIKIMEDIA_PASSWORD: ${{ secrets.WIKIMEDIA_PASSWORD }}
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 }}
MWMCONTINUE: ${{ inputs.map-generator-continue }}
MWMCOUNTRIES: ${{ inputs.map-generator-countries }}
DEBIAN_FRONTEND: noninteractive
TZ: Etc/UTC
jobs:
copy-coasts:
if: inputs.jobs == 'copy-coasts' || inputs.jobs == 'all'
name: Copy Previously Generated Coasts
clone-repos:
name: Clone Git Repos
runs-on: mapfilemaker
container:
image: ubuntu:latest
image: codeberg.org/comaps/maps_generator:f6d53d54f794
volumes:
- /media/4tbexternal:/media/4tbexternal
- /mnt/4tbexternal:/mnt/4tbexternal
concurrency:
group: ${{ github.workflow }}-map-generator-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
steps:
- uses: actions/cache@v4
with:
path: "~"
key: cache-${{ github.run_id }}-${{ github.run_attempt }}
- name: Checkout main repo
shell: bash
run: |
echo "Cloning $FORGEJO_SERVER_URL/$FORGEJO_REPOSITORY branch $FORGEJO_REF_NAME"
cd ~
git clone --recurse-submodules --shallow-submodules -b $FORGEJO_REF_NAME --single-branch $FORGEJO_SERVER_URL/$FORGEJO_REPOSITORY.git comaps
- name: Checkout wikiparser repo
shell: bash
run: |
cd ~
git clone https://codeberg.org/comaps/wikiparser.git
- name: Checkout subways repo
shell: bash
run: |
cd ~
git clone https://codeberg.org/comaps/subways.git
copy-coasts:
if: inputs.jobs == 'copy-coasts' || inputs.jobs == 'all-except-upload'
name: Copy Previously Generated Coasts
runs-on: mapfilemaker
needs:
- clone-repos
container:
image: codeberg.org/comaps/maps_generator:f6d53d54f794
volumes:
- /mnt/4tbexternal/:/mnt/4tbexternal/
- /mnt/4tbexternal/osm-planet:/home/planet
concurrency:
group: ${{ github.workflow }}-map-generator-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
@@ -47,336 +99,403 @@ 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*
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'
if: inputs.jobs == 'planet' || inputs.jobs == 'all-except-upload'
name: Update Planet
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
shell: bash
run: |
apt-get update -y
apt-get install -y pyosmium osmium-tool python3-venv python3-pip wget2
rm -f /usr/lib/python*/EXTERNALLY-MANAGED
pip3 install "protobuf<4"
- name: Download Planet File if Absent
shell: bash
# 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
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
# TODO: better to run osmupdate (not convert) just before starting the maps jobs - for max fresh data.
run: |
echo "Starting..."
cd /home/planet/planet/
osmconvert -v --drop-author --drop-version --hash-memory=4000 planet-latest.osm.pbf -o=planet.o5m
echo "Done."
- name: Notify Zulip
run: |
curl -X POST https://comaps.zulipchat.com/api/v1/messages \
-u $ZULIP_BOT_EMAIL:$ZULIP_API_KEY \
--data-urlencode type=stream \
--data-urlencode 'to="DevOps"' \
--data-urlencode topic=codeberg-bot \
--data-urlencode 'content=Planet update is done!'
wiki-update:
if: inputs.jobs == 'wiki' || inputs.jobs == 'all'
if: inputs.jobs == 'wiki' || inputs.jobs == 'all-except-upload'
name: Update Wikipedia
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
shell: bash
run: |
apt-get update -y
apt-get install -y jq curl wget2 rustc cargo git ca-certificates
- name: Clone wikiparser if necessary
shell: bash
run: |
if [ ! -d /media/4tbexternal/wikiparser ]; then
cd /media/4tbexternal
git clone https://codeberg.org/comaps/wikiparser.git
fi
- 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
# Check all .tar.gz files in /home/planet/wikipedia/dumps/latest/
for file in /home/planet/wikipedia/dumps/latest/*.tar.gz; do
# Check if file exists (handles case where glob doesn't match)
[ -e "$file" ] || continue
# Get file size in MB and modification time in days
size_mb=$(stat -f%z "$file" 2>/dev/null | awk '{print int($1/1024/1024)}' || stat -c%s "$file" | awk
'{print int($1/1024/1024)}')
days_old=$(find "$file" -mtime -7 | wc -l)
# Verify conditions
if [ "$size_mb" -lt 100 ]; then
echo "FAIL: $file is only ${size_mb}MB (< 100MB)"
FAILCHECK=1
elif [ "$days_old" -eq 0 ]; then
echo "FAIL: $file is older than 7 days"
ls -al $file
FAILCHECK=1
else
echo "PASS: $file (${size_mb}MB, modified within 7 days)"
fi
done
exit $FAILCHECK
- name: Notify Zulip
run: |
curl -X POST https://comaps.zulipchat.com/api/v1/messages \
-u $ZULIP_BOT_EMAIL:$ZULIP_API_KEY \
--data-urlencode type=stream \
--data-urlencode 'to="DevOps"' \
--data-urlencode topic=codeberg-bot \
--data-urlencode 'content=Wiki update is done!'
update-isolines:
if: inputs.jobs == 'isolines' || inputs.jobs == 'all'
if: inputs.jobs == 'isolines' || inputs.jobs == 'all-except-upload'
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
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
- 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 /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 \
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=../osm-planet/isolines/tmp-tiles/ \
--countries_isolines_out_dir=../osm-planet/isolines/ \
--tiles_isolines_out_dir=/home/planet/isolines/tmp-tiles/ \
--countries_isolines_out_dir=/home/planet/isolines/ \
--data_dir=./data/ \
--srtm_path=../osm-planet/SRTM-patched-europe/ \
--threads=22
--srtm_path=/home/planet/SRTM-patched-europe/ \
--threads=96
- name: Check isolines
shell: bash
run: |
NUMISO=$(ls -al /home/planet/isolines/*.isolines | wc -l)
echo "Found $NUMISO isolines"
if [ $NUMISO -lt 10 ]; then
echo "ERROR: Did generation fail?"
exit 1
fi
- name: Notify Zulip
run: |
curl -X POST https://comaps.zulipchat.com/api/v1/messages \
-u $ZULIP_BOT_EMAIL:$ZULIP_API_KEY \
--data-urlencode type=stream \
--data-urlencode 'to="DevOps"' \
--data-urlencode topic=codeberg-bot \
--data-urlencode 'content=Isolines are done!'
update-subways:
if: inputs.jobs == 'subways' || inputs.jobs == 'all'
if: inputs.jobs == 'subways' || inputs.jobs == 'all-except-upload'
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
shell: bash
run: |
apt-get update -qq && apt-get install -y --no-install-recommends curl osmctools osmium-tool python3-venv ca-certificates git python3-pip
rm -f /usr/lib/python*/EXTERNALLY-MANAGED
pip3 install "protobuf<4"
- name: Clone subways if necessary
shell: bash
run: |
if [ ! -d /media/4tbexternal/subways ]; then
cd /media/4tbexternal
git clone https://codeberg.org/comaps/subways.git
fi
- name: Clone main repo if necessary
shell: bash
run: |
if [ ! -d /media/4tbexternal/comaps-init ]; then
cd /media/4tbexternal
git clone --recurse-submodules --shallow-submodules -b rebase-generator-pastk-wb251014 --single-branch https://codeberg.org/comaps/comaps.git comaps-init
fi
- uses: actions/cache@v4
with:
path: "~"
key: cache-${{ github.run_id }}-${{ github.run_attempt }}
- name: Update Subways
shell: bash
run: |
cd /media/4tbexternal/comaps-init/
cd ~/comaps/
cp tools/unix/maps/settings.sh.prod tools/unix/maps/settings.sh
./tools/unix/maps/generate_subways.sh
- name: Notify Zulip
run: |
curl -X POST https://comaps.zulipchat.com/api/v1/messages \
-u $ZULIP_BOT_EMAIL:$ZULIP_API_KEY \
--data-urlencode type=stream \
--data-urlencode 'to="DevOps"' \
--data-urlencode topic=codeberg-bot \
--data-urlencode 'content=Subways are done!'
update-tiger:
if: inputs.jobs == 'tiger' || inputs.jobs == 'all'
if: inputs.jobs == 'tiger' || inputs.jobs == 'all-except-upload'
name: Update TIGER
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
shell: bash
run: |
apt-get update -qq && apt-get install -y --no-install-recommends \
build-essential \
clang \
cmake \
ninja-build \
ca-certificates \
git \
wget2
- name: Clone main repo if necessary
shell: bash
run: |
if [ ! -d /media/4tbexternal/comaps-init ]; then
cd /media/4tbexternal
git clone --recurse-submodules --shallow-submodules -b rebase-generator-pastk-wb251014 --single-branch https://codeberg.org/comaps/comaps.git comaps-init
fi
- uses: actions/cache@v4
with:
path: "~"
key: cache-${{ github.run_id }}-${{ github.run_attempt }}
- name: Build address_parser
shell: bash
run: |
cd /media/4tbexternal/comaps-init
rm -rf ../omim-build-relwithdebinfo/CMakeCache.txt
rm -rf ../omim-build-relwithdebinfo/CMakeFiles
./tools/unix/build_omim.sh -R address_parser_tool
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: |
cd /media/4tbexternal/osm-planet/
# 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
tar -xOzf tiger-nominatim-preprocessed-latest.csv.tar.gz | /media/4tbexternal/omim-build-relwithdebinfo/address_parser_tool --output_path=./tiger
cd ~/comaps
tar -xOzf /home/planet/tiger-nominatim-preprocessed-latest.csv.tar.gz | ~/omim-build-relwithdebinfo/address_parser_tool --output_path=/home/planet/tiger
generate-maps:
if: inputs.jobs == 'maps' || inputs.jobs == 'all'
if: inputs.jobs == 'maps' || inputs.jobs == 'all-except-upload'
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.jobs == 'upload'
name: Upload Maps
runs-on: mapfilemaker
container:
image: codeberg.org/comaps/maps_generator:f6d53d54f794
volumes:
- /mnt/4tbexternal/:/mnt/4tbexternal/
- /mnt/4tbexternal/osm-planet:/home/planet
concurrency:
group: ${{ github.workflow }}-map-generator-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
steps:
- name: Write config file
run: |
mkdir -p ~/.config/rclone/
echo "${{ secrets.RCLONE_CONF }}" > ~/.config/rclone/rclone.conf
- name: Upload map files to CDNs
shell: bash
run: |
shopt -s nullglob
buildfolder=$(find /mnt/4tbexternal/osm-maps/ -mindepth 1 -maxdepth 1 -iname "2*" -type d | sort -n -r | head -1 | cut -d/ -f5)
builddate=$(find /mnt/4tbexternal/osm-maps/*/ -mindepth 1 -maxdepth 1 -iname "2*" -type d | sort -n -r | head -1 | cut -d/ -f6)
mwmfiles=( /mnt/4tbexternal/osm-maps/$buildfolder/$builddate/*.mwm )
if (( ${#mwmfiles[@]} )); then
echo "<$(date +%T)> Uploading maps from $buildfolder/$builddate..."
cd ~/comaps/tools/unix/maps
./upload_to_cdn.sh /mnt/4tbexternal/osm-maps/$buildfolder/$builddate
echo "<$(date +%T)> Finished uploading maps from $buildfolder/$builddate."
else
echo "<$(date +%T)> No MWM files in /mnt/4tbexternal/osm-maps/$buildfolder/$builddate/*.mwm, not uploading maps."
echo "<$(date +%T)> Found top level: $(ls -alt /mnt/4tbexternal/osm-maps/*)"
echo "<$(date +%T)> Found second level: $(ls -alt /mnt/4tbexternal/osm-maps/$buildfolder/*)"
fi
- name: Notify Zulip
run: |
curl -X POST https://comaps.zulipchat.com/api/v1/messages \
-u $ZULIP_BOT_EMAIL:$ZULIP_API_KEY \
--data-urlencode type=stream \
--data-urlencode 'to="DevOps"' \
--data-urlencode topic=codeberg-bot \
--data-urlencode 'content=Upload is done!'

1
.gitignore vendored
View File

@@ -9,6 +9,7 @@ Makefile.Release
object_script.*.Debug
object_script.*.Release
compile_commands.json
*.local.*
stxxl.errlog
stxxl.log

View File

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

View File

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

View File

@@ -1,8 +1,11 @@
• 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 16. November
Bäume hinzugefügt
Kleine Bushaltestellen-Icons die früher angezeigt werden
• Sichtbarkeit von Eingängen reduziert
Neue POI-Typen hinzugefügt
Sandflächen werden auf Karte angezeigt
Option „Geschäft ist leerstehend” zum OSM-Editor hinzugefügt
• Straßenbeschilderung in Europa verbessert
• Option „Asphaltierte Straßen vermeiden” hinzugefügt
• Icons in den Einstellungen hinzugefügt
Weitere Änderungen in unseren Codeberg-Versionshinweisen!

Binary file not shown.

Before

Width:  |  Height:  |  Size: 628 KiB

After

Width:  |  Height:  |  Size: 636 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 532 KiB

After

Width:  |  Height:  |  Size: 407 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 391 KiB

After

Width:  |  Height:  |  Size: 155 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

After

Width:  |  Height:  |  Size: 454 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 268 KiB

After

Width:  |  Height:  |  Size: 451 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 263 KiB

After

Width:  |  Height:  |  Size: 357 KiB

View File

@@ -1,8 +1,11 @@
• 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
• OpenStreetMap data as of November 16
Added trees
Made bus stop icons smaller and show up earlier
Reduce visibility of entrances
Added several other POI types
Show sand areas on the map
Add business is vacant option to the OSM editor
• Improved road shields in Europe
• Avoid paved roads routing option
• Added icons to the settings page
Check our Codeberg release notes for more changes!

View File

@@ -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
Nuevos datos OSM a 16.11.25
Se añaden árboles
Se reducen los iconos de paradas de autobús y aparecen antes
Se reduce visibilidad de entradas
Se añaden muchos tipos de puntos de interés
Se muestran las zonas de arena en el mapa
• Se añade la opción «negocio vacío» al editor
• Se mejoran las señales de tráfico en Europa
• Nueva opción «evitar caminos asfaltadas»
• Nuevos iconos en la página de configuración
Más detalles en Codeberg

Binary file not shown.

Before

Width:  |  Height:  |  Size: 655 KiB

After

Width:  |  Height:  |  Size: 605 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 532 KiB

After

Width:  |  Height:  |  Size: 407 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 391 KiB

After

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

After

Width:  |  Height:  |  Size: 452 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 254 KiB

After

Width:  |  Height:  |  Size: 460 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 263 KiB

After

Width:  |  Height:  |  Size: 355 KiB

View File

@@ -1,8 +1,11 @@
• 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
• Données OpenStreetMap du 16 novembre
Ajout des arbres
Les icônes des arrêts de bus sont plus petites et apparaissent plus tôt
Réduction de la visibilité des entrées
Ajout de nouveaux types de POI
Affichage des zones de sable
Nouveau bouton pour signaler un commerce vacant
• Amélioration des badges routiers en Europe
• Ajout d'une option pour éviter les routes pavées
• Ajout d'icônes dans les paramètres
Plus d'informations sur notre Codeberg

View File

@@ -0,0 +1,11 @@
• Mappe OSM al 16 Novembre
• Aggiunti alberi
• Fermate bus più piccole e visibili prima
• Entrate rese meno ingombranti
• Aggiunti diversi POI
• Le zone sabbiose sono ora visibili
• Aggiunta l'opzione di POI vacante nell'editor OSM
• Migliorate le insegne stradali in Europa
• Aggiunta opzione per evitare strade pavimentate
• Aggiunte icone nelle impostazioni
Visita Codeberg per ulteriori dettagli

View File

@@ -1,7 +1,11 @@
• 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 OSM de 16/11
Adição de árvores ao mapa
Ícones de pontos de ônibus menores e exibidos mais cedo
Visibilidade reduzida das entradas
Adição de diversos outros tipos de Pontos de Interesse
Exibição de áreas de areia no mapa
• Adição de opção de "Estabelecimento vazio" ao editor do OSM
• Melhorias nas placas de rodovias na Europa
• Opção para evitar rotas por estradas pavimentadas
• Adição de ícones nas configurações
Confira as notas de lançamento do Codeberg para mais detalhes!

View File

@@ -1,8 +1,10 @@
• Карты OpenStreetMap от 4 ноября
Обновлены цвета иконок на карте, добавлены новые цвета для развлечений, спорта, некоторых бизнесов
На зарядных станциях показываются имеющиеся типы разъёмов
Добавлены эстрады, скамейки без спинок и лежаки
Новые иконки для разных спорт центров, массажных салонов, гостевых домов, некоторых закрытых бизнесов
Несколько улучшений и исправлений в поиске
Улучшены голосовые подсказки при навигации
• Карты OpenStreetMap от 16 ноября
Добавлены деревья и песчаные области
Автобусные остановки показываются раньше, но маленькими иконами
Входы разделены по типам
В OSM редакторе можно отметить место как неиспользуемое
Улучшения в иконках номеров дорог в Европе
Возможность избегать дорог с покрытием при построении маршрута
• Иконки в экране настроек
На карту добавлены несколько новых типов объектов
Подробнее смотрите на codeberg.org/comaps/comaps/releases

View File

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

View File

@@ -0,0 +1,11 @@
• Подaци са OpenStreetMap-а закључно са 16. новембром
• Додате ознаке за дрвеће
• Смањене иконе аутобуских стајалишта и ранији приказ
• Смањена видљивост улаза
• Додато неколико нових типова ознака на мапу
• Приказ пешчаних површина на мапи
• Додата опција „пословни простор је празан“ у OSM едитору
• Побољшани путокази у Европи
• Опција рутирања „избегавај асфалтиране путеве“
• Додате иконе на страници подешавања
Погледајте детаље на Codeberg-у за списак свих промена!

View File

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

View File

@@ -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>
உங்கள் பயணத்தைக் கண்டறியவும், தனியுரிமை மற்றும் சமூகத்தை முன்னணியில் கொண்டு உலகிற்கு செல்லவும்!

View File

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

View File

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

View File

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

View File

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

View File

@@ -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());

View File

@@ -32,7 +32,8 @@ public class DrivingOptionsScreen extends BaseMapScreen
new DrivingOption(RoadType.Dirty, R.string.avoid_unpaved),
new DrivingOption(RoadType.Ferry, R.string.avoid_ferry),
new DrivingOption(RoadType.Motorway, R.string.avoid_motorways),
new DrivingOption(RoadType.Steps, R.string.avoid_steps)};
new DrivingOption(RoadType.Steps, R.string.avoid_steps),
new DrivingOption(RoadType.Paved, R.string.avoid_paved)};
@NonNull
private final Map<RoadType, Boolean> mInitialDrivingOptionsState = new HashMap<>();

View File

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

View File

@@ -4,6 +4,7 @@ import android.location.Location;
import android.text.TextUtils;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.Nullable;
import androidx.core.view.ViewCompat;
import app.organicmaps.MwmActivity;
@@ -26,6 +27,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 +38,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;
@@ -43,8 +49,10 @@ public class OnmapDownloader implements MwmActivity.LeftAnimationTrackListener
@Override
public void onStatusChanged(List<MapManager.StorageCallbackData> data)
{
if (mCurrentCountry == null)
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,12 @@ 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 +122,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 +207,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 +266,7 @@ public class OnmapDownloader implements MwmActivity.LeftAnimationTrackListener
public void onResume()
{
updateOfflineExplanationVisibility();
if (mStorageSubscriptionSlot == 0)
{
mStorageSubscriptionSlot = MapManager.nativeSubscribe(mStorageCallback);

View File

@@ -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;
@@ -827,6 +828,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 +897,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 +944,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 +1022,19 @@ public class EditorFragment extends BaseMwmFragment implements View.OnClickListe
dialogFragment.setTextSaveListener(this::commitPlaceDoesntExists);
}
private void placeDisused()
{
new MaterialAlertDialogBuilder(requireActivity(), R.style.MwmTheme_AlertDialog)
.setTitle(R.string.editor_mark_business_vacant_title)
.setMessage(R.string.editor_mark_business_vacant_description)
.setPositiveButton(R.string.editor_submit, (dlg, which) -> {
Editor.nativeMarkPlaceAsDisused();
mParent.processEditedFeatures();
})
.setNegativeButton(android.R.string.cancel, null)
.show();
}
private void commitPlaceDoesntExists(@NonNull String text)
{
Editor.nativePlaceDoesNotExist(text);

View File

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

View File

@@ -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,7 @@ import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
public class LanguagesFragment extends BaseMwmRecyclerFragment<LanguagesAdapter>
@@ -32,10 +36,23 @@ public class LanguagesFragment extends BaseMwmRecyclerFragment<LanguagesAdapter>
Set<String> existingLanguages =
args != null ? new HashSet<>(args.getStringArrayList(EXISTING_LOCALIZED_NAMES)) : new HashSet<>();
Configuration config = requireContext().getResources().getConfiguration();
LocaleListCompat systemLocales = ConfigurationCompat.getLocales(config);
List<Language> languages = new ArrayList<>();
List<Language> systemLanguages = new ArrayList<>();
for (Language lang : Editor.nativeGetSupportedLanguages(false))
{
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(lang);
}
if (existingLanguages.contains(lang.code) || systemLanguages.contains(lang))
continue;
languages.add(lang);
@@ -43,6 +60,8 @@ public class LanguagesFragment extends BaseMwmRecyclerFragment<LanguagesAdapter>
Collections.sort(languages, Comparator.comparing(lhs -> lhs.name));
languages.addAll(0, systemLanguages);
return new LanguagesAdapter(this, languages.toArray(new Language[languages.size()]));
}

View File

@@ -1,5 +1,6 @@
package app.organicmaps.editor;
import android.content.res.ColorStateList;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
@@ -11,6 +12,7 @@ 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;
@@ -381,6 +383,29 @@ class SimpleTimetableAdapter extends RecyclerView.Adapter<SimpleTimetableAdapter
final boolean enable = mComplementItem != null && mComplementItem.weekdays.length != 0;
final String text = mFragment.getString(R.string.editor_time_add);
mAdd.setEnabled(enable);
final ColorStateList bgButtonColor = new ColorStateList(
new int[][]{
new int[]{android.R.attr.state_enabled}, // enabled
new int[]{-android.R.attr.state_enabled} // disabled
},
new int[]{
ContextCompat.getColor(
mAdd.getContext(), R.color.base_accent),
ContextCompat.getColor(mAdd.getContext(), R.color.button_accent_disabled)
});
final ColorStateList textButtonColor = new ColorStateList(
new int[][]{
new int[]{android.R.attr.state_enabled}, // enabled
new int[]{-android.R.attr.state_enabled} // disabled
},
new int[]{
ContextCompat.getColor(
mAdd.getContext(),
UiUtils.getStyledResourceId(mAdd.getContext(), android.R.attr.textColorPrimaryInverse)),
ContextCompat.getColor(mAdd.getContext(), R.color.button_accent_text_disabled)
});
mAdd.setBackgroundTintList(bgButtonColor);
mAdd.setTextColor(textButtonColor);
mAdd.setText(enable ? text + " (" + TimeFormatUtils.formatWeekdays(mComplementItem) + ")" : text);
}
}

View File

@@ -21,6 +21,9 @@ import app.organicmaps.util.UiUtils;
class SearchAdapter extends RecyclerView.Adapter<SearchAdapter.SearchDataViewHolder>
{
private static final int SHORT_HORIZON_CLOSE_MIN = 60;
private static final int SHORT_HORIZON_OPEN_MIN = 15;
private final SearchFragment mSearchFragment;
@Nullable
private SearchResult[] mResults;
@@ -149,41 +152,32 @@ 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);
UiUtils.setTextAndShow(mOpen, string);
mOpen.setTextColor(ContextCompat.getColor(mSearchFragment.getContext(), R.color.base_yellow));
}
else
{
UiUtils.setTextAndShow(mOpen, resources.getString(R.string.editor_time_open));
mOpen.setTextColor(ContextCompat.getColor(mSearchFragment.getContext(), R.color.base_green));
}
// Hide if unknown opening hours state
UiUtils.hide(mOpen);
return;
}
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));
}
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));
}
default -> UiUtils.hide(mOpen);
else
{
UiUtils.setTextAndShow(mOpen, isOpen ? resources.getString(R.string.editor_time_open) : resources.getString(R.string.closed));
mOpen.setTextColor(ContextCompat.getColor(mSearchFragment.getContext(), isOpen ? R.color.base_green : R.color.base_red));
}
}

View File

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

View File

@@ -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);
}
}

View File

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

View File

@@ -31,6 +31,8 @@ import androidx.fragment.app.FragmentFactory;
import androidx.fragment.app.FragmentManager;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
import app.organicmaps.BuildConfig;
import app.organicmaps.MwmActivity;
import app.organicmaps.MwmApplication;
import app.organicmaps.R;
@@ -85,9 +87,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 +109,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);
@@ -797,57 +804,127 @@ 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)
{
final Context context = requireContext();
final OhState poiState = OpeningHours.nativeCurrentState(timetables);
UiUtils.hide(mTvOpenState);
return;
}
// Ignore unknown rule state
if (poiState.state == OhState.State.Unknown)
final Context context = requireContext();
final OhState poiState = OpeningHours.nativeCurrentState(timetables);
// Ignore unknown rule state
if (poiState.state == OhState.State.Unknown)
{
UiUtils.hide(mTvOpenState);
return;
}
// Get colours
final ForegroundColorSpan colorGreen =
new ForegroundColorSpan(ContextCompat.getColor(context, R.color.base_green));
final ForegroundColorSpan colorYellow =
new ForegroundColorSpan(ContextCompat.getColor(context, R.color.base_yellow));
final ForegroundColorSpan colorRed = new ForegroundColorSpan(ContextCompat.getColor(context, R.color.base_red));
// Get next state info
final SpannableStringBuilder openStateString = new SpannableStringBuilder();
final boolean isOpen = (poiState.state == OhState.State.Open); // False == Closed due to early exit for Unknown
final long nextStateTime = isOpen ? poiState.nextTimeClosed : poiState.nextTimeOpen; // Unix time (seconds)
ZonedDateTime nextChangeLocal = null;
boolean hasFiniteNextChange = false;
final long nowSec = System.currentTimeMillis() / 1000;
final int minsToNextState = (int) ((nextStateTime - nowSec) / 60);
// Try to resolve a finite next-change time; handle 24/7 case
final boolean looksLike247 = "24/7".equals(ohStr.trim());
final int ONE_WEEK_MIN = 7 * 24 * 60;
final boolean noRealNextChange = looksLike247 || minsToNextState >= ONE_WEEK_MIN;
if (!noRealNextChange)
{
try
{
UiUtils.hide(mTvOpenState);
return;
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;
}
}
catch (Throwable ignored) {}
}
// Get colours
final ForegroundColorSpan colorGreen =
new ForegroundColorSpan(ContextCompat.getColor(context, R.color.base_green));
final ForegroundColorSpan colorYellow =
new ForegroundColorSpan(ContextCompat.getColor(context, R.color.base_yellow));
final ForegroundColorSpan colorRed = new ForegroundColorSpan(ContextCompat.getColor(context, R.color.base_red));
// Get next state info
final SpannableStringBuilder openStateString = new SpannableStringBuilder();
final boolean isOpen = (poiState.state == OhState.State.Open); // False == Closed due to early exit for Unknown
final long nextStateTime = isOpen ? poiState.nextTimeClosed : poiState.nextTimeOpen; // Unix time (seconds)
final int minsToNextState = (int) ((nextStateTime - (System.currentTimeMillis() / 1000)) / 60);
if (minsToNextState <= 60) // POI opens/closes in 60 mins
{
final String minsToChangeStr = minsToNextState + " " + getString(R.string.minute);
final String nextChangeFormatted = getString(isOpen ? R.string.closes_in : R.string.opens_in, minsToChangeStr);
final ForegroundColorSpan nextChangeColor = isOpen ? colorYellow : colorRed;
// TODO: We should check closed/open time for specific feature's timezone.
ZonedDateTime time = ZonedDateTime.ofInstant(Instant.ofEpochSecond(nextStateTime), ZoneId.systemDefault());
String localizedTime =
new HoursMinutes(time.getHour(), time.getMinute(), DateUtils.is24HourFormat(context)).toString();
openStateString.append(nextChangeFormatted, nextChangeColor, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
.append("") // Add spacer
.append(getString(R.string.at, localizedTime));
}
else if (isOpen)
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()

View File

@@ -4,7 +4,7 @@
<size
android:width="40dp"
android:height="40dp" />
<solid android:color="#717065" />
<solid android:color="#51585E" />
</shape>
</item>
<item>

View File

@@ -4,7 +4,7 @@
<size
android:width="40dp"
android:height="40dp" />
<solid android:color="#717065" />
<solid android:color="#51585E" />
</shape>
</item>
<item>

View File

@@ -4,7 +4,7 @@
<size
android:width="40dp"
android:height="40dp" />
<solid android:color="#7F5933" />
<solid android:color="#802D19" />
</shape>
</item>
<item>

View File

@@ -4,7 +4,7 @@
<size
android:width="40dp"
android:height="40dp" />
<solid android:color="#BB9342" />
<solid android:color="#8C491C" />
</shape>
</item>
<item>

View File

@@ -4,7 +4,7 @@
<size
android:width="40dp"
android:height="40dp" />
<solid android:color="#7F5933" />
<solid android:color="#802D19" />
</shape>
</item>
<item>

View File

@@ -4,7 +4,7 @@
<size
android:width="40dp"
android:height="40dp" />
<solid android:color="#8C5F93" />
<solid android:color="#6B425C" />
</shape>
</item>
<item>

View File

@@ -4,7 +4,7 @@
<size
android:width="40dp"
android:height="40dp" />
<solid android:color="#717065" />
<solid android:color="#574469" />
</shape>
</item>
<item>

View File

@@ -4,7 +4,7 @@
<size
android:width="40dp"
android:height="40dp" />
<solid android:color="#C15746" />
<solid android:color="#983E44" />
</shape>
</item>
<item>

View File

@@ -4,7 +4,7 @@
<size
android:width="40dp"
android:height="40dp" />
<solid android:color="#664E42" />
<solid android:color="#614A43" />
</shape>
</item>
<item>

View File

@@ -4,7 +4,7 @@
<size
android:width="40dp"
android:height="40dp" />
<solid android:color="#BB9342" />
<solid android:color="#8C491C" />
</shape>
</item>
<item>

View File

@@ -4,7 +4,7 @@
<size
android:width="40dp"
android:height="40dp" />
<solid android:color="#2E89B0" />
<solid android:color="#20607C" />
</shape>
</item>
<item>

View File

@@ -4,7 +4,7 @@
<size
android:width="40dp"
android:height="40dp" />
<solid android:color="#C15746" />
<solid android:color="#983E44" />
</shape>
</item>
<item>

View File

@@ -4,7 +4,7 @@
<size
android:width="40dp"
android:height="40dp" />
<solid android:color="#717065" />
<solid android:color="#51585E" />
</shape>
</item>
<item>

View File

@@ -4,7 +4,7 @@
<size
android:width="40dp"
android:height="40dp" />
<solid android:color="#717065" />
<solid android:color="#51585E" />
</shape>
</item>
<item>

View File

@@ -5,7 +5,7 @@
<size
android:width="40dp"
android:height="40dp" />
<solid android:color="#717065" />
<solid android:color="#51585E" />
</shape>
</item>
<item>

View File

@@ -4,7 +4,7 @@
<size
android:width="40dp"
android:height="40dp" />
<solid android:color="#717065" />
<solid android:color="#51585E" />
</shape>
</item>
<item>

View File

@@ -4,7 +4,7 @@
<size
android:width="40dp"
android:height="40dp" />
<solid android:color="#8C5F93" />
<solid android:color="#6B425C" />
</shape>
</item>
<item>

View File

@@ -4,7 +4,7 @@
<size
android:width="40dp"
android:height="40dp" />
<solid android:color="#8C5F93" />
<solid android:color="#6B425C" />
</shape>
</item>
<item>

View File

@@ -4,7 +4,7 @@
<size
android:width="40dp"
android:height="40dp" />
<solid android:color="#7F5933" />
<solid android:color="#51585E" />
</shape>
</item>
<item>

View File

@@ -4,7 +4,7 @@
<size
android:width="40dp"
android:height="40dp" />
<solid android:color="#7F5933" />
<solid android:color="#6E4426" />
</shape>
</item>
<item>

View File

@@ -4,7 +4,7 @@
<size
android:width="40dp"
android:height="40dp" />
<solid android:color="#717065" />
<solid android:color="#51585E" />
</shape>
</item>
<item>

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?colorControlNormal">
<path
android:pathData="M80,360v-160q0,-33 23.5,-56.5T160,120h640q33,0 56.5,23.5T880,200v160h-80v-160L160,200v160L80,360ZM160,720q-33,0 -56.5,-23.5T80,640v-200h80v200h640v-200h80v200q0,33 -23.5,56.5T800,720L160,720ZM40,840v-80h880v80L40,840ZM480,420ZM80,440v-80h240q11,0 21,6t15,16l47,93 123,-215q5,-9 14,-14.5t20,-5.5q11,0 21,5.5t15,16.5l49,98h235v80L620,440q-11,0 -21,-5.5T584,418l-26,-53 -123,215q-5,10 -15,15t-21,5q-11,0 -20.5,-6T364,578l-69,-138L80,440Z"
android:fillColor="#fff"/>
</vector>

View File

@@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="@android:color/white"
android:pathData="M480,536L284,732Q273,743 256,743Q239,743 228,732Q217,721 217,704Q217,687 228,676L424,480L228,284Q217,273 217,256Q217,239 228,228Q239,217 256,217Q273,217 284,228L480,424L676,228Q687,217 704,217Q721,217 732,228Q743,239 743,256Q743,273 732,284L536,480L732,676Q743,687 743,704Q743,721 732,732Q721,743 704,743Q687,743 676,732L480,536Z"/>
</vector>

View File

@@ -4,7 +4,7 @@
<size
android:width="40dp"
android:height="40dp" />
<solid android:color="#9C6136" />
<solid android:color="#EB785D" />
</shape>
</item>
<item>

View File

@@ -4,7 +4,7 @@
<size
android:width="40dp"
android:height="40dp" />
<solid android:color="#9C6136" />
<solid android:color="#EB785D" />
</shape>
</item>
<item>

View File

@@ -4,7 +4,7 @@
<size
android:width="40dp"
android:height="40dp" />
<solid android:color="#747E86" />
<solid android:color="#AA96BC" />
</shape>
</item>
<item>

View File

@@ -4,7 +4,7 @@
<size
android:width="40dp"
android:height="40dp" />
<solid android:color="#9C6136" />
<solid android:color="#747E86" />
</shape>
</item>
<item>

View File

@@ -1,5 +1,10 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="#FFFFFF"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="@android:color/white"
android:pathData="M480,536L284,732Q273,743 256,743Q239,743 228,732Q217,721 217,704Q217,687 228,676L424,480L228,284Q217,273 217,256Q217,239 228,228Q239,217 256,217Q273,217 284,228L480,424L676,228Q687,217 704,217Q721,217 732,228Q743,239 743,256Q743,273 732,284L536,480L732,676Q743,687 743,704Q743,721 732,732Q721,743 704,743Q687,743 676,732L480,536Z"/>
</vector>

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?colorControlNormal">
<path
android:pathData="M480,840q-150,0 -255,-105T120,480q0,-150 105,-255t255,-105q14,0 27.5,1t26.5,3q-41,29 -65.5,75.5T444,300q0,90 63,153t153,63q55,0 101,-24.5t75,-65.5q2,13 3,26.5t1,27.5q0,150 -105,255T480,840ZM480,760q88,0 158,-48.5T740,585q-20,5 -40,8t-40,3q-123,0 -209.5,-86.5T364,300q0,-20 3,-40t8,-40q-78,32 -126.5,102T200,480q0,116 82,198t198,82ZM470,490Z"
android:fillColor="#fff"/>
</vector>

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?colorControlNormal">
<path
android:pathData="M19,9h-4v-6h-6v6h-4l7,7 7,-7ZM5,18v2h14v-2h-14Z"
android:fillColor="#FFF"/>
</vector>

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?colorControlNormal">
<path
android:pathData="M216,784q-45,-45 -70.5,-104T120,558q0,-63 24,-124.5T222,318q35,-35 86.5,-60t122,-39.5Q501,204 591.5,201t202.5,7q8,106 5,195t-16.5,160.5q-13.5,71.5 -38,125T684,778q-53,53 -112.5,77.5T450,880q-65,0 -127,-25.5T216,784ZM328,768q29,17 59.5,24.5T450,800q46,0 91,-18.5t86,-59.5q18,-18 36.5,-50.5t32,-85Q709,534 716,459.5t2,-177.5q-49,-2 -110.5,-1.5T485,290q-61,9 -116,29t-90,55q-45,45 -62,89t-17,85q0,59 22.5,103.5T262,714q42,-80 111,-153.5T534,440q-72,63 -125.5,142.5T328,768ZM328,768ZM328,768Z"
android:fillColor="#fff"/>
</vector>

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?colorControlNormal">
<path
android:pathData="M200,800v-280h-80v-80h240v80h-80v280h-80ZM200,360v-200h80v200h-80ZM360,360v-80h80v-120h80v120h80v80L360,360ZM440,800v-360h80v360h-80ZM680,800v-120h-80v-80h240v80h-80v120h-80ZM680,520v-360h80v360h-80Z"
android:fillColor="#fff"/>
</vector>

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?colorControlNormal">
<path
android:pathData="M280,920q-33,0 -56.5,-23.5T200,840v-720q0,-33 23.5,-56.5T280,40h400q33,0 56.5,23.5T760,120v124q18,7 29,22t11,34v80q0,19 -11,34t-29,22v404q0,33 -23.5,56.5T680,920L280,920ZM280,840h400v-720L280,120v720ZM280,840v-720,720ZM394,640h172q14,0 24,-10t10,-24v-132q0,-14 -10,-24t-24,-10h-6v-40q0,-33 -23.5,-56.5T480,320q-33,0 -56.5,23.5T400,400v40h-6q-14,0 -24,10t-10,24v132q0,14 10,24t24,10ZM440,440v-40q0,-17 11.5,-28.5T480,360q17,0 28.5,11.5T520,400v40h-80Z"
android:fillColor="#fff"/>
</vector>

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?colorControlNormal">
<path
android:pathData="M339,698q22,-22 49.5,-36t58.5,-19q-5,19 -8,38t-3,39q0,24 3.5,47t11.5,44L339,698ZM254,614 L170,528q62,-62 142,-95t168,-33q49,0 96,10.5t90,30.5q-44,8 -81.5,29T517,522q-9,-1 -18.5,-1.5T480,520q-64,0 -122.5,24.5T254,614ZM84,444 L0,360q95,-97 219.5,-148.5T480,160q136,0 260.5,51.5T960,360l-84,84q-79,-79 -181.5,-121.5T480,280q-112,0 -214.5,42.5T84,444ZM760,520 L772,580q12,5 22.5,10.5T816,604l58,-18 40,68 -46,40q2,12 2,26t-2,26l46,40 -40,68 -58,-18q-11,8 -21.5,13.5T772,860l-12,60h-80l-12,-60q-12,-5 -22.5,-10.5T624,836l-58,18 -40,-68 46,-40q-2,-12 -2,-26t2,-26l-46,-40 40,-68 58,18q11,-8 21.5,-13.5T668,580l12,-60h80ZM720,640q-33,0 -56.5,23.5T640,720q0,33 23.5,56.5T720,800q33,0 56.5,-23.5T800,720q0,-33 -23.5,-56.5T720,640Z"
android:fillColor="#fff"/>
</vector>

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?colorControlNormal">
<path
android:pathData="M480,480q-66,0 -113,-47t-47,-113q0,-66 47,-113t113,-47q66,0 113,47t47,113q0,66 -47,113t-113,47ZM160,800v-112q0,-34 17.5,-62.5T224,582q62,-31 126,-46.5T480,520q66,0 130,15.5T736,582q29,15 46.5,43.5T800,688v112L160,800ZM240,720h480v-32q0,-11 -5.5,-20T700,654q-54,-27 -109,-40.5T480,600q-56,0 -111,13.5T260,654q-9,5 -14.5,14t-5.5,20v32ZM480,400q33,0 56.5,-23.5T560,320q0,-33 -23.5,-56.5T480,240q-33,0 -56.5,23.5T400,320q0,33 23.5,56.5T480,400ZM480,320ZM480,720Z"
android:fillColor="#fff"/>
</vector>

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?colorControlNormal">
<path
android:pathData="m798,638 l-62,-62q44,-41 69,-97t25,-119q0,-63 -25,-118t-69,-96l62,-64q56,53 89,125t33,153q0,81 -33,153t-89,125ZM670,510l-64,-64q18,-17 29,-38.5t11,-47.5q0,-26 -11,-47.5T606,274l64,-64q32,29 50,67.5t18,82.5q0,44 -18,82.5T670,510ZM360,520q-66,0 -113,-47t-47,-113q0,-66 47,-113t113,-47q66,0 113,47t47,113q0,66 -47,113t-113,47ZM40,840v-112q0,-33 17,-62t47,-44q51,-26 115,-44t141,-18q77,0 141,18t115,44q30,15 47,44t17,62v112L40,840ZM120,760h480v-32q0,-11 -5.5,-20T580,694q-36,-18 -92.5,-36T360,640q-71,0 -127.5,18T140,694q-9,5 -14.5,14t-5.5,20v32ZM360,440q33,0 56.5,-23.5T440,360q0,-33 -23.5,-56.5T360,280q-33,0 -56.5,23.5T280,360q0,33 23.5,56.5T360,440ZM360,360ZM360,760Z"
android:fillColor="#fff"/>
</vector>

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?colorControlNormal">
<path
android:pathData="M360,440h80v-160h-80v160ZM480,440h80v-160h-80v160ZM600,440h80v-160h-80v160ZM240,880q-33,0 -56.5,-23.5T160,800v-480l240,-240h320q33,0 56.5,23.5T800,160v640q0,33 -23.5,56.5T720,880L240,880ZM240,800h480v-640L434,160L240,354v446ZM240,800h480,-480Z"
android:fillColor="#fff"/>
</vector>

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M13.26,3C8.17,2.86 4,6.95 4,12L2.21,12c-0.45,0 -0.67,0.54 -0.35,0.85l2.79,2.8c0.2,0.2 0.51,0.2 0.71,0l2.79,-2.8c0.31,-0.31 0.09,-0.85 -0.36,-0.85L6,12c0,-3.9 3.18,-7.05 7.1,-7 3.72,0.05 6.85,3.18 6.9,6.9 0.05,3.91 -3.1,7.1 -7,7.1 -1.61,0 -3.1,-0.55 -4.28,-1.48 -0.4,-0.31 -0.96,-0.28 -1.32,0.08 -0.42,0.42 -0.39,1.13 0.08,1.49C9,20.29 10.91,21 13,21c5.05,0 9.14,-4.17 9,-9.26 -0.13,-4.69 -4.05,-8.61 -8.74,-8.74zM12.75,8c-0.41,0 -0.75,0.34 -0.75,0.75v3.68c0,0.35 0.19,0.68 0.49,0.86l3.12,1.85c0.36,0.21 0.82,0.09 1.03,-0.26 0.21,-0.36 0.09,-0.82 -0.26,-1.03l-2.88,-1.71v-3.4c0,-0.4 -0.34,-0.74 -0.75,-0.74z"/>
</vector>

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?colorControlNormal">
<path
android:pathData="M480,560q-33,0 -56.5,-23.5T400,480q0,-33 23.5,-56.5T480,400q33,0 56.5,23.5T560,480q0,33 -23.5,56.5T480,560ZM480,840q-139,0 -241,-91.5T122,520h82q14,104 92.5,172T480,760q117,0 198.5,-81.5T760,480q0,-117 -81.5,-198.5T480,200q-69,0 -129,32t-101,88h110v80L120,400v-240h80v94q51,-64 124.5,-99T480,120q75,0 140.5,28.5t114,77q48.5,48.5 77,114T840,480q0,75 -28.5,140.5t-77,114q-48.5,48.5 -114,77T480,840Z"
android:fillColor="#fff"/>
</vector>

View File

@@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M2,20h20v-4h-20v4ZM4,17h2v2h-2v-2ZM2,4v4h20v-4h-20ZM6,7h-2v-2h2v2ZM2,14h20v-4h-20v4ZM4,11h2v2h-2v-2Z"
android:fillColor="#FFF"/>
</vector>

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?colorControlNormal">
<path
android:pathData="M160,720q-33,0 -56.5,-23.5T80,640v-320q0,-33 23.5,-56.5T160,240h640q33,0 56.5,23.5T880,320v320q0,33 -23.5,56.5T800,720L160,720ZM160,640h640v-320L680,320v160h-80v-160h-80v160h-80v-160h-80v160h-80v-160L160,320v320ZM280,480h80,-80ZM440,480h80,-80ZM600,480h80,-80ZM480,480Z"
android:fillColor="#fff"/>
</vector>

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?colorControlNormal">
<path
android:pathData="M280,800v-520L80,280v-120h520v120L400,280v520L280,800ZM640,800v-320L520,480v-120h360v120L760,480v320L640,800Z"
android:fillColor="#fff"/>
</vector>

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?colorControlNormal">
<path
android:pathData="M480,880q-83,0 -156,-31.5T197,763q-54,-54 -85.5,-127T80,480h80q0,115 72.5,203T418,794l-58,-58 56,-56L598,862q-29,10 -58.5,14T480,880ZM500,600v-240h120q17,0 28.5,11.5T660,400v160q0,17 -11.5,28.5T620,600L500,600ZM300,600v-60h100v-40h-60v-40h60v-40L300,420v-60h120q17,0 28.5,11.5T460,400v160q0,17 -11.5,28.5T420,600L300,600ZM560,540h40v-120h-40v120ZM800,480q0,-115 -72.5,-203T542,166l58,58 -56,56 -182,-182q29,-10 58.5,-14t59.5,-4q83,0 156,31.5T763,197q54,54 85.5,127T880,480h-80Z"
android:fillColor="#fff"/>
</vector>

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?colorControlNormal">
<path
android:pathData="m476,880 l182,-480h84L924,880h-84l-43,-122L603,758L560,880h-84ZM160,760l-56,-56 202,-202q-35,-35 -63.5,-80T190,320h84q20,39 40,68t48,58q33,-33 68.5,-92.5T484,240L40,240v-80h280v-80h80v80h280v80L564,240q-21,72 -63,148t-83,116l96,98 -30,82 -122,-125 -202,201ZM628,688h144l-72,-204 -72,204Z"
android:fillColor="#fff"/>
</vector>

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?colorControlNormal">
<path
android:pathData="m476,880 l182,-480h84L924,880h-84l-43,-122L603,758L560,880h-84ZM628,688h144l-72,-204 -72,204ZM254,640q-66,0 -123.5,-38.5T44,498l72,-36q21,42 58,70t79,28q38,0 62.5,-23.5T340,480q0,-33 -23.5,-56.5T260,400h-60v-80h60q25,0 42.5,-17.5T320,260q0,-25 -17,-42.5T261,200q-23,0 -41,15t-32,33l-63,-49q26,-32 60,-55.5t77,-23.5q57,0 97.5,40.5T400,259q0,27 -10,52.5T361,357q10,10 18.5,20.5T396,400h124v-200h-80v-80h240v80h-80v116l-61,164L420,480v4q0,63 -46,109.5T254,640Z"
android:fillColor="#fff"/>
</vector>

View File

@@ -3,7 +3,13 @@
android:height="20dp"
android:viewportWidth="960"
android:viewportHeight="960">
<group
android:pivotX="370"
android:pivotY="480"
android:scaleX="2.2"
android:scaleY="1.7">
<path
android:pathData="M420,652q-8,0 -14,-5.5t-6,-14.5v-304q0,-9 6,-14.5t14,-5.5q2,0 14,6l145,145q5,5 7,10t2,11q0,6 -2,11t-7,10L434,646q-3,3 -6.5,4.5T420,652Z"
android:fillColor="#1f1f1f"/>
android:pathData="M640,760 L200,480l440,-280v560Z"
android:fillColor="#FFFFFF"/>
</group>
</vector>

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?colorControlNormal">
<path
android:pathData="M480,840 L0,359q93,-93 215.5,-146T480,160q142,0 264.5,53T960,359l-56,57q-81,-81 -190,-128.5T480,240q-103,0 -195,32.5T117,363l419,420 -56,57ZM864,800L761,698q-18,11 -38,16.5t-43,5.5q-68,0 -114,-46t-46,-114q0,-68 46,-114t114,-46q68,0 114,46t46,114q0,23 -5.5,43T818,641l102,103 -56,56ZM680,640q34,0 57,-23t23,-57q0,-34 -23,-57t-57,-23q-34,0 -57,23t-23,57q0,34 23,57t57,23ZM480,783Z"
android:fillColor="#fff"/>
</vector>

View File

@@ -26,27 +26,33 @@
android:clipChildren="false"
android:gravity="center"
tools:ignore="UselessParent">
<com.google.android.material.textview.MaterialTextView
<LinearLayout
android:id="@+id/offline_explanation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/margin_eighth"
android:gravity="center_horizontal"
android:textStyle="bold"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="@string/offline_explanation_title"/>
<com.google.android.material.textview.MaterialTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/margin_eighth"
android:gravity="center_horizontal"
android:textStyle="normal"
android:textAppearance="@style/MwmTextAppearance.Body1"
android:text="@string/offline_explanation_text"/>
<com.google.android.material.divider.MaterialDivider
android:layout_width="match_parent"
android:layout_height="2dp"
android:layout_marginTop="@dimen/margin_quarter"
android:layout_marginBottom="@dimen/margin_quarter"/>
android:orientation="vertical">
<com.google.android.material.textview.MaterialTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/margin_eighth"
android:gravity="center_horizontal"
android:textStyle="bold"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="@string/offline_explanation_title"/>
<com.google.android.material.textview.MaterialTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/margin_eighth"
android:gravity="center_horizontal"
android:textStyle="normal"
android:textAppearance="@style/MwmTextAppearance.Body1"
android:text="@string/offline_explanation_text"/>
<com.google.android.material.divider.MaterialDivider
android:layout_width="match_parent"
android:layout_height="2dp"
android:layout_marginTop="@dimen/margin_quarter"
android:layout_marginBottom="@dimen/margin_quarter"/>
</LinearLayout>
<com.google.android.material.textview.MaterialTextView
android:id="@+id/downloader_parent"
android:layout_width="wrap_content"

View File

@@ -3,8 +3,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/feedback_fab"
style="MwmWidget.M3.FAB.Primary"
app:shapeAppearance="@style/ShapeAppearanceOverlay.Material3.FloatingActionButton"
style="@style/MwmWidget.M3.FAB.Primary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/feedback"

View File

@@ -22,8 +22,8 @@
android:clipChildren="false">
<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/floating_triangle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_width="@dimen/margin_half"
android:layout_height="@dimen/margin_half"
android:elevation="@dimen/margin_eighth"
android:layout_centerVertical="true"
app:tint="@android:color/white"

View File

@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:background="?attr/cardBackground"
android:orientation="vertical"
android:layout_width="match_parent"
@@ -20,8 +21,15 @@
android:minHeight="@dimen/height_block_base"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingEnd="@dimen/margin_base"
android:paddingEnd="@dimen/margin_half"
android:paddingStart="@dimen/margin_base">
<com.google.android.material.imageview.ShapeableImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginEnd="@dimen/margin_base_plus"
android:layout_marginTop="@dimen/margin_half_plus"
app:srcCompat="@drawable/ic_avoid_tolls_disabled"
app:tint="?iconTint" />
<com.google.android.material.textview.MaterialTextView
android:text="@string/avoid_tolls"
android:textAppearance="?android:attr/textAppearanceMedium"
@@ -36,14 +44,20 @@
android:layout_height="match_parent"
android:padding="@dimen/margin_half_double_plus"/>
</LinearLayout>
<include layout="@layout/item_divider"/>
<LinearLayout
android:orientation="horizontal"
android:minHeight="@dimen/height_block_base"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingEnd="@dimen/margin_base"
android:paddingEnd="@dimen/margin_half"
android:paddingStart="@dimen/margin_base">
<com.google.android.material.imageview.ShapeableImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginEnd="@dimen/margin_base_plus"
android:layout_marginTop="@dimen/margin_half_plus"
app:srcCompat="@drawable/ic_avoid_unpaved_disabled"
app:tint="?iconTint" />
<com.google.android.material.textview.MaterialTextView
android:text="@string/avoid_unpaved"
android:textAppearance="?android:attr/textAppearanceMedium"
@@ -58,14 +72,48 @@
android:layout_height="match_parent"
android:padding="@dimen/margin_half_double_plus"/>
</LinearLayout>
<include layout="@layout/item_divider"/>
<LinearLayout
android:orientation="horizontal"
android:minHeight="@dimen/height_block_base"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingEnd="@dimen/margin_base"
android:paddingEnd="@dimen/margin_half"
android:paddingStart="@dimen/margin_base">
<com.google.android.material.imageview.ShapeableImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginEnd="@dimen/margin_base_plus"
android:layout_marginTop="@dimen/margin_half_plus"
app:srcCompat="@drawable/ic_avoid_paved_disabled"
app:tint="?iconTint" />
<com.google.android.material.textview.MaterialTextView
android:text="@string/avoid_paved"
android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:textColor="?android:attr/textColorPrimary"/>
<androidx.appcompat.widget.SwitchCompat
android:id="@+id/avoid_paved_roads_btn"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:padding="@dimen/margin_half_double_plus"/>
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:minHeight="@dimen/height_block_base"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingEnd="@dimen/margin_half"
android:paddingStart="@dimen/margin_base">
<com.google.android.material.imageview.ShapeableImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginEnd="@dimen/margin_base_plus"
android:layout_marginTop="@dimen/margin_half_plus"
app:srcCompat="@drawable/ic_avoid_ferry_disabled"
app:tint="?iconTint" />
<com.google.android.material.textview.MaterialTextView
android:text="@string/avoid_ferry"
android:textAppearance="?android:attr/textAppearanceMedium"
@@ -80,14 +128,20 @@
android:layout_height="match_parent"
android:padding="@dimen/margin_half_double_plus"/>
</LinearLayout>
<include layout="@layout/item_divider"/>
<LinearLayout
android:orientation="horizontal"
android:minHeight="@dimen/height_block_base"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingEnd="@dimen/margin_base"
android:paddingEnd="@dimen/margin_half"
android:paddingStart="@dimen/margin_base">
<com.google.android.material.imageview.ShapeableImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginEnd="@dimen/margin_base_plus"
android:layout_marginTop="@dimen/margin_half_plus"
app:srcCompat="@drawable/ic_avoid_motorways_disabled"
app:tint="?iconTint" />
<com.google.android.material.textview.MaterialTextView
android:text="@string/avoid_motorways"
android:textAppearance="?android:attr/textAppearanceMedium"
@@ -102,14 +156,20 @@
android:padding="@dimen/margin_half_double_plus"
android:layout_height="match_parent"/>
</LinearLayout>
<include layout="@layout/item_divider"/>
<LinearLayout
android:orientation="horizontal"
android:minHeight="@dimen/height_block_base"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingEnd="@dimen/margin_base"
android:paddingEnd="@dimen/margin_half"
android:paddingStart="@dimen/margin_base">
<com.google.android.material.imageview.ShapeableImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginEnd="@dimen/margin_base_plus"
android:layout_marginTop="@dimen/margin_half_plus"
app:srcCompat="@drawable/ic_avoid_steps_disabled"
app:tint="?iconTint" />
<com.google.android.material.textview.MaterialTextView
android:text="@string/avoid_steps"
android:textAppearance="?android:attr/textAppearanceMedium"

View File

@@ -394,7 +394,8 @@
<com.google.android.material.card.MaterialCardView
android:id="@+id/cv__more"
style="@style/MwmWidget.Editor.CardView">
style="@style/MwmWidget.Editor.CardView"
android:layout_marginBottom="@dimen/margin_base">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -421,6 +422,17 @@
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
<com.google.android.material.button.MaterialButton
android:id="@+id/disused"
style="@style/MwmWidget.M3.Button.Secondary"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:layout_marginBottom="@dimen/margin_quarter"
app:backgroundTint="?cardBackground"
android:textColor="@color/base_red"
app:strokeColor="@color/base_red"
android:text="@string/editor_business_vacant_button"/>
<com.google.android.material.button.MaterialButton
android:id="@+id/reset"
style="@style/MwmWidget.M3.Button.Secondary"

View File

@@ -22,7 +22,7 @@
android:id="@+id/empty_opening_hours"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginTop="@dimen/margin_base"
android:layout_marginStart="@dimen/editor_margin_timetable_left"
android:orientation="vertical"
tools:visibility="gone">
@@ -57,10 +57,8 @@
android:layout_height="wrap_content"
style="@style/Widget.Material3.Button.TextButton"
android:layout_below="@id/opening_hours"
android:layout_marginStart="@dimen/editor_margin_timetable_left"
android:layout_marginTop="@dimen/margin_base_plus"
android:gravity="center_vertical"
android:minHeight="@dimen/height_block_base"
android:layout_marginTop="@dimen/margin_base"
android:gravity="center_horizontal"
android:text="@string/edit_opening_hours"
android:textColor="?colorSecondary"
android:textAppearance="@style/MwmTextAppearance.Body1" />

View File

@@ -28,12 +28,12 @@
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/delete_icon"
app:layout_constraintStart_toEndOf="@+id/phone_icon"
app:hintEnabled="false"
app:layout_constraintTop_toTopOf="parent">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/input"
style="@style/MwmWidget.Editor.FieldLayout.EditText"
tools:hint="Hint"
tools:text="Input"
android:hint="@string/phone_format"
android:inputType="phone"/>
</com.google.android.material.textfield.TextInputLayout>

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