Compare commits
306 commits
2025.01.26
...
master
Author | SHA1 | Date | |
---|---|---|---|
f4366688b7 | |||
90772d66d2 | |||
cf00843b0a | |||
|
58546f3115 | ||
|
cddfc2e891 | ||
|
2bdf6763ce | ||
|
a54f5268cd | ||
|
466b9365f6 | ||
6bf8338cdd | |||
|
95a2cf3bc5 | ||
cf7cce69fa | |||
d95bfeb0fd | |||
575e17d63c | |||
|
bac9073bcb | ||
|
e2f0b8b258 | ||
|
840fa7d7f7 | ||
d7db7df723 | |||
e8cbb31005 | |||
aaa40a9b77 | |||
cd2f801fe6 | |||
|
dcaca3ecb5 | ||
4421a7b6e8 | |||
5a1e518c3c | |||
4a2f4a9f03 | |||
|
028720a452 | ||
|
1f043478df | ||
|
bef5ed25ca | ||
|
6bcc35d982 | ||
|
de536b96e9 | ||
|
62cb82c09f | ||
|
a773bf7cc6 | ||
|
8c371fb509 | ||
|
a887d930f7 | ||
|
e0a8a1f1f8 | ||
|
1d69dfe8ce | ||
|
8b93625010 | ||
|
4fe769be75 | ||
|
f3c472c23a | ||
|
fcb2a3af29 | ||
|
0143ac3538 | ||
|
b6e9181c98 | ||
|
20d608f850 | ||
|
dccf7b97fe | ||
55a903903b | |||
c29584acbe | |||
|
7ae108ed6e | ||
|
d3713cd8ca | ||
2b29b484a5 | |||
7e79f90e7f | |||
|
e7c16d75ae | ||
|
51e47b12d2 | ||
|
033cc84649 | ||
|
f4862bbeff | ||
|
22577a0a57 | ||
73fd45af68 | |||
|
d9e6ebf809 | ||
028c4b28a3 | |||
cf06b21e53 | |||
ce97f195d7 | |||
d3a4e8e7b2 | |||
91b607276f | |||
|
7f49ad21fe | ||
|
bd1b2f5c93 | ||
|
538dd1bf1e | ||
|
c6efc5dba1 | ||
58c5d87455 | |||
f3acc898cd | |||
53a39be6f6 | |||
15a4a80583 | |||
d18843f344 | |||
|
e7d97f467b | ||
|
2759fed166 | ||
423c54d21c | |||
51e565feed | |||
|
510980c2f2 | ||
d16cceb9c2 | |||
2f1917dc04 | |||
8d05a39efe | |||
6b2f8db1d7 | |||
|
0c0af7b029 | ||
|
e9f32a77ea | ||
|
334752facf | ||
|
4d9514b510 | ||
|
e739a2f5c3 | ||
|
8db2a0136e | ||
|
baed033cf0 | ||
7d58b65a11 | |||
|
ceba407373 | ||
|
2c766f64ba | ||
8e885b38e3 | |||
7a149d3d22 | |||
fa3b4cb661 | |||
27bd379ac3 | |||
d93eba8cc7 | |||
10ffea348d | |||
6ef933de5b | |||
f4be439d29 | |||
|
90b8ed428f | ||
178bdf9f27 | |||
150b83b677 | |||
6022a07532 | |||
eb5a4916ae | |||
00548ab8cb | |||
6e37398cf1 | |||
058644ecef | |||
173b7e4515 | |||
ac1cbbfa2a | |||
3b3e063ef9 | |||
6eab4f9537 | |||
1753a8bc87 | |||
d21800ec25 | |||
62778dbfbe | |||
540bd70d96 | |||
c23d782d6f | |||
c7b2b7d136 | |||
d2f8a03468 | |||
5344ed5940 | |||
|
97fc3606f9 | ||
|
13071a609c | ||
b14939e770 | |||
32bf3a3e9a | |||
147e12360c | |||
7bfbb517cf | |||
|
33ca7570ad | ||
644b9da2b1 | |||
c52a3e3c09 | |||
44f3475b90 | |||
c5019f0252 | |||
|
018dfb0cbc | ||
|
a11c266584 | ||
|
08b9e407cb | ||
2c002eb08b | |||
c96d873fa8 | |||
|
40e1e63315 | ||
a9d85463ab | |||
55951a28be | |||
52bbb9c28a | |||
a4067bc128 | |||
806ef7e749 | |||
|
57e72f93bc | ||
d6d7a33bdf | |||
6b458d4f65 | |||
e49e836f42 | |||
d281139fd6 | |||
80413f97e1 | |||
4f6ee70bf8 | |||
3925b423a1 | |||
e767b0f454 | |||
cc216c4895 | |||
4b15a12893 | |||
8dfd9a789c | |||
377d10de8c | |||
f9fb0e8d7f | |||
fbc1ebd84d | |||
5db61f0498 | |||
4eb7bf3f73 | |||
fcaad4d98e | |||
f42083b8c9 | |||
c666a68b37 | |||
e53dcf7716 | |||
ca2d888744 | |||
|
cd832b94b6 | ||
1cbe25e5bc | |||
db4371e2c3 | |||
|
9e47a49ec0 | ||
83994a247e | |||
98ef2d61fb | |||
524f3fe358 | |||
34510b8f1c | |||
0687642095 | |||
b9d0138cd1 | |||
cf7a71b590 | |||
1ea96c4069 | |||
5ffd95a491 | |||
69953c2b12 | |||
|
868b538435 | ||
|
ca29e0b563 | ||
25a6c1a5b1 | |||
fced7be257 | |||
|
eacb3bbfa2 | ||
|
b2464b2d31 | ||
462b73ef5a | |||
1e0065ab4d | |||
4a82b560bd | |||
8df6183ba7 | |||
9c7438cb69 | |||
645f17c441 | |||
234dfb6d57 | |||
f5e61c7f35 | |||
b0f96bf094 | |||
|
09127e75c8 | ||
|
e9326e99e4 | ||
|
ce09fe33d4 | ||
fa52e1b3d4 | |||
|
1a3a527af3 | ||
|
af8af7a8a5 | ||
|
313664dc1c | ||
|
75e5cd9168 | ||
9240602fa5 | |||
|
04eb9642bb | ||
83df1e5004 | |||
8905269239 | |||
1783c90714 | |||
|
bc6a9e4804 | ||
|
2331171850 | ||
|
cb6af94e12 | ||
|
9678e0c491 | ||
|
4c9ac07829 | ||
|
d6f1d1174c | ||
|
1e31f4c8c0 | ||
|
76584aaa52 | ||
5b5a27900a | |||
e44d8a19a3 | |||
2290fc88c0 | |||
|
abdb58ba62 | ||
8cc91e7372 | |||
|
b831eba92d | ||
29cf6abd7d | |||
8b02809b5d | |||
9a02f4e3fd | |||
d688c855a1 | |||
38ed6221c2 | |||
16cae17c50 | |||
944a61a52c | |||
8a3ff104a8 | |||
5e144a189a | |||
8f8a4449a7 | |||
042e8ce025 | |||
72536b7c98 | |||
4d40e3dfac | |||
bb240c1aaf | |||
82dd6bb98a | |||
|
1bc3490ca7 | ||
e1128b51fc | |||
1790a4dfca | |||
5841d0f96e | |||
2b746f0bf7 | |||
0597afd475 | |||
291daa91c0 | |||
bfa15962df | |||
|
1f0a6760e0 | ||
6b6b7d145e | |||
9c8b6d934c | |||
19701d83e6 | |||
d8a594855a | |||
|
0a063dce44 | ||
|
d9de3eee6e | ||
|
87949cdb8e | ||
|
3106cbd66d | ||
|
f3b5f72d93 | ||
3c52bf59fd | |||
c349f39fe8 | |||
0f4b3eadd4 | |||
a07a6d2d57 | |||
0edc581acf | |||
199dd10c51 | |||
55aba70d2b | |||
23521ca309 | |||
d9a8fb1934 | |||
2d2a27dec7 | |||
8057e8c632 | |||
e89f80644f | |||
6703faaae2 | |||
49fce04798 | |||
7f45e7ffd7 | |||
6c1b069de7 | |||
45dd46799e | |||
|
98ba6f66a9 | ||
65f556e175 | |||
945ae37209 | |||
c851828162 | |||
84a49920cc | |||
|
fb3f6e539c | ||
|
b3a56d8b6c | ||
|
431cf357a3 | ||
|
3493538c56 | ||
|
1664c549b4 | ||
|
882cfd1a89 | ||
457dac6f15 | |||
efcadd6f25 | |||
3ce6a9a29d | |||
598bb5f176 | |||
|
4d7572e07c | ||
519a851a66 | |||
5c3a0c28cf | |||
659af4bf1d | |||
9b93d66f3d | |||
6ed297c62e | |||
db1b38b40f | |||
f815ec1a17 | |||
6dd2ea68d6 | |||
|
d5635fd27b | ||
2f004d2d05 | |||
622e5d3a83 | |||
73511395b0 | |||
|
2ff9c4c690 | ||
2b271761c1 | |||
bd07332971 | |||
|
9ff69ede4f | ||
7777a78448 | |||
e02ae76196 | |||
2b82c234e0 | |||
|
4b889f5b72 | ||
|
0c9399e28c | ||
|
b8848eb39b | ||
7865ecb18b |
870 changed files with 160996 additions and 276056 deletions
101
.github/CODEOWNERS
vendored
101
.github/CODEOWNERS
vendored
|
@ -1,66 +1,69 @@
|
|||
# All non-assigned.
|
||||
* @organicmaps/mergers
|
||||
# Visual design.
|
||||
android/app/src/main/res/drawable*/ @organicmaps/design
|
||||
android/app/src/main/res/font/ @organicmaps/design
|
||||
android/app/src/main/res/mipmap*/ @organicmaps/design
|
||||
data/*.ttf @organicmaps/design
|
||||
data/resources*/ @organicmaps/design
|
||||
data/search-icons/ @organicmaps/design
|
||||
data/styles/default/light/**/*.png @organicmaps/design
|
||||
data/styles/default/light/**/*.svg @organicmaps/design
|
||||
data/styles/default/dark/**/*.png @organicmaps/design
|
||||
data/styles/default/dark/**/*.svg @organicmaps/design
|
||||
iphone/Maps/Images.xcassets/ @organicmaps/design
|
||||
/android/app/src/main/res/drawable*/ @organicmaps/design
|
||||
/android/app/src/main/res/font/ @organicmaps/design
|
||||
/android/app/src/main/res/mipmap*/ @organicmaps/design
|
||||
/data/*.ttf @organicmaps/design
|
||||
/data/resources-svg/ @organicmaps/design
|
||||
/data/search-icons/ @organicmaps/design
|
||||
/iphone/Maps/Images.xcassets/ @organicmaps/design
|
||||
# Android.
|
||||
android/ @organicmaps/android
|
||||
android/app/src/main/java/app/organicmaps/car/ @organicmaps/android-auto
|
||||
docs/ANDROID_LOCATION_TEST.md @organicmaps/android
|
||||
docs/JAVA_STYLE.md @organicmaps/android
|
||||
/android/ @organicmaps/android
|
||||
/android/app/src/main/java/app/organicmaps/car/ @organicmaps/android-auto
|
||||
/docs/ANDROID_LOCATION_TEST.md @organicmaps/android
|
||||
/docs/JAVA_STYLE.md @organicmaps/android
|
||||
# no owner for translation changes
|
||||
/android/app/src/main/res/values*/strings.xml
|
||||
# iOS.
|
||||
iphone/ @organicmaps/ios
|
||||
xcode/ @organicmaps/ios
|
||||
docs/OBJC_STYLE.md @organicmaps/ios
|
||||
/iphone/ @organicmaps/ios
|
||||
/xcode/ @organicmaps/ios
|
||||
/docs/OBJC_STYLE.md @organicmaps/ios
|
||||
# no owner for translation changes
|
||||
/iphone/plist.txt
|
||||
/iphone/Maps/LocalizedStrings/
|
||||
# Qt
|
||||
qt/ @organicmaps/qt
|
||||
/qt/ @organicmaps/qt
|
||||
# Rendering
|
||||
drape/ @organicmaps/rendering
|
||||
drape_frontend/ @organicmaps/rendering
|
||||
/drape/ @organicmaps/rendering
|
||||
/drape_frontend/ @organicmaps/rendering
|
||||
# Map Data.
|
||||
tools/python/maps_generator/ @organicmaps/data
|
||||
generator/ @organicmaps/data
|
||||
topography_generator/ @organicmaps/data
|
||||
data/borders/ @organicmaps/data
|
||||
data/conf/isolines/ @organicmaps/data
|
||||
docs/SUBWAY_GENERATION.md @organicmaps/data
|
||||
docs/MAPS.md @organicmaps/data
|
||||
docs/EXPERIMENTAL_PUBLIC_TRANSPORT_SUPPORT.md @organicmaps/data
|
||||
/tools/python/maps_generator/ @organicmaps/data
|
||||
/generator/ @organicmaps/data
|
||||
/topography_generator/ @organicmaps/data
|
||||
/data/borders/ @organicmaps/data
|
||||
/data/conf/isolines/ @organicmaps/data
|
||||
/docs/SUBWAY_GENERATION.md @organicmaps/data
|
||||
/docs/MAPS.md @organicmaps/data
|
||||
/docs/EXPERIMENTAL_PUBLIC_TRANSPORT_SUPPORT.md @organicmaps/data
|
||||
# no owner (changed often to add a new POI)
|
||||
/generator/generator_tests/osm_type_test.cpp
|
||||
# Map Styles.
|
||||
data/styles/ @organicmaps/styles
|
||||
data/types.txt @organicmaps/styles
|
||||
data/visibility.txt @organicmaps/styles
|
||||
data/mapcss-mapping.csv @organicmaps/styles
|
||||
data/replaced_tags.txt @organicmaps/styles
|
||||
data/classificator.txt @organicmaps/styles
|
||||
data/drules_* @organicmaps/styles
|
||||
docs/STYLES.md
|
||||
tools/kothic/ @organicmaps/styles
|
||||
/data/styles/ @organicmaps/styles
|
||||
/data/types.txt @organicmaps/styles
|
||||
/data/visibility.txt @organicmaps/styles
|
||||
/data/mapcss-mapping.csv @organicmaps/styles
|
||||
/data/replaced_tags.txt @organicmaps/styles
|
||||
/data/classificator.txt @organicmaps/styles
|
||||
/data/drules_* @organicmaps/styles
|
||||
/docs/STYLES.md
|
||||
/tools/kothic/ @organicmaps/styles
|
||||
# DevOps.
|
||||
.github/workflows @organicmaps/devops
|
||||
android/*gradle* @organicmaps/devops
|
||||
docs/RELEASE_MANAGEMENT.md @organicmaps/devops
|
||||
xcode/fastlane/ @organicmaps/devops
|
||||
/.github/workflows @organicmaps/devops
|
||||
/android/*gradle* @organicmaps/devops
|
||||
/docs/RELEASE_MANAGEMENT.md @organicmaps/devops
|
||||
/xcode/fastlane/ @organicmaps/devops
|
||||
# Growth.
|
||||
README.md @organicmaps/growth
|
||||
.github/FUNDING.yml @organicmaps/growth
|
||||
android/app/src/fdroid/play/ @organicmaps/growth
|
||||
android/app/src/google/play/ @organicmaps/growth
|
||||
iphone/metadata/ @organicmaps/growth
|
||||
/.github/FUNDING.yml @organicmaps/growth
|
||||
/android/app/src/fdroid/play/ @organicmaps/growth
|
||||
/android/app/src/google/play/ @organicmaps/growth
|
||||
/iphone/metadata/ @organicmaps/growth
|
||||
# Legal.
|
||||
LEGAL @organicmaps/legal
|
||||
LICENSE @organicmaps/legal
|
||||
NOTICE @organicmaps/legal
|
||||
CONTRIBUTORS @organicmaps/legal
|
||||
docs/CODE_OF_CONDUCT.md @organicmaps/legal
|
||||
docs/DCO.md @organicmaps/legal
|
||||
docs/GOVERNANCE.md @organicmaps/legal
|
||||
/docs/CODE_OF_CONDUCT.md @organicmaps/legal
|
||||
/docs/DCO.md @organicmaps/legal
|
||||
/docs/GOVERNANCE.md @organicmaps/legal
|
||||
|
|
8
.github/workflows/linux-check.yaml
vendored
8
.github/workflows/linux-check.yaml
vendored
|
@ -61,6 +61,10 @@ jobs:
|
|||
libgl1-mesa-dev \
|
||||
libglvnd-dev \
|
||||
libharfbuzz-dev \
|
||||
libxrandr-dev \
|
||||
libxinerama-dev \
|
||||
libxcursor-dev \
|
||||
libxi-dev \
|
||||
qt6-base-dev \
|
||||
libqt6svg6-dev \
|
||||
qt6-positioning-dev \
|
||||
|
@ -128,6 +132,10 @@ jobs:
|
|||
libgl1-mesa-dev \
|
||||
libglvnd-dev \
|
||||
libharfbuzz-dev \
|
||||
libxrandr-dev \
|
||||
libxinerama-dev \
|
||||
libxcursor-dev \
|
||||
libxi-dev \
|
||||
qt6-base-dev \
|
||||
libqt6svg6-dev \
|
||||
qt6-positioning-dev \
|
||||
|
|
22
.github/workflows/stale.yml
vendored
Normal file
22
.github/workflows/stale.yml
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
name: Close stale PRs
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 0 * * *" # Runs every day at midnight
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/stale@v9
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
days-before-pr-stale: 180 # 6 months before warning
|
||||
days-before-pr-close: 365 # Closed after 12 months
|
||||
stale-pr-label: "stale"
|
||||
stale-pr-message: "Hi! This PR has been inactive for 6 months. If it's still relevant, please update it to let us know you’d like to keep it open 😊"
|
||||
close-pr-message: "This PR has been automatically closed after 12 months of inactivity."
|
||||
days-before-issue-stale: -1 # Issues are never stale
|
||||
days-before-issue-close: -1 # Issues are never closed
|
||||
remove-stale-when-updated: true
|
31
.github/workflows/strings-check.yaml
vendored
31
.github/workflows/strings-check.yaml
vendored
|
@ -1,31 +0,0 @@
|
|||
name: Validate translation strings
|
||||
on:
|
||||
workflow_dispatch: # Manual trigger
|
||||
pull_request:
|
||||
paths:
|
||||
- .github/workflows/strings-check.yaml # Run check on self change
|
||||
- data/strings/strings.txt
|
||||
- data/strings/types_strings.txt
|
||||
- data/strings/sound.txt
|
||||
- data/countries_names.txt
|
||||
- iphone/plist.txt
|
||||
- tools/python/strings_utils.py
|
||||
|
||||
jobs:
|
||||
validate-translation-strings:
|
||||
name: Validate translation strings
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3'
|
||||
|
||||
- name: Validate string files
|
||||
shell: bash
|
||||
run: |
|
||||
for f in data/strings/strings.txt data/strings/types_strings.txt data/strings/sound.txt data/countries_names.txt iphone/plist.txt; do
|
||||
./tools/python/strings_utils.py --validate $f -o
|
||||
done;
|
||||
git diff --exit-code
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -20,6 +20,7 @@ data/drules_proto_default_design.bin
|
|||
data/colors_design.txt
|
||||
data/patterns_design.txt
|
||||
data/bookmarks
|
||||
data/edits.xml
|
||||
|
||||
# Compiled Python
|
||||
*.pyc
|
||||
|
|
12
.gitmodules
vendored
12
.gitmodules
vendored
|
@ -7,9 +7,6 @@
|
|||
[submodule "3party/protobuf/protobuf"]
|
||||
path = 3party/protobuf/protobuf
|
||||
url = https://github.com/organicmaps/protobuf.git
|
||||
[submodule "tools/twine"]
|
||||
path = tools/twine
|
||||
url = https://github.com/organicmaps/twine.git
|
||||
[submodule "3party/Vulkan-Headers"]
|
||||
path = 3party/Vulkan-Headers
|
||||
url = https://github.com/KhronosGroup/Vulkan-Headers.git
|
||||
|
@ -59,3 +56,12 @@
|
|||
[submodule "3party/utfcpp"]
|
||||
path = 3party/utfcpp
|
||||
url = https://github.com/nemtrif/utfcpp.git
|
||||
[submodule "3party/glfw"]
|
||||
path = 3party/glfw
|
||||
url = https://github.com/glfw/glfw.git
|
||||
[submodule "3party/CMake-MetalShaderSupport"]
|
||||
path = 3party/CMake-MetalShaderSupport
|
||||
url = https://github.com/dpogue/CMake-MetalShaderSupport.git
|
||||
[submodule "3party/imgui/imgui"]
|
||||
path = 3party/imgui/imgui
|
||||
url = https://github.com/ocornut/imgui
|
||||
|
|
1
3party/CMake-MetalShaderSupport
Submodule
1
3party/CMake-MetalShaderSupport
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 989857d2e5e54869c35ad06fb21a67d12a2dbc67
|
|
@ -66,4 +66,19 @@ add_subdirectory(vulkan_wrapper)
|
|||
|
||||
if (PLATFORM_DESKTOP)
|
||||
add_subdirectory(libtess2)
|
||||
|
||||
set(GLFW_BUILD_DOCS OFF CACHE BOOL "")
|
||||
set(GLFW_BUILD_EXAMPLES OFF CACHE BOOL "")
|
||||
set(GLFW_BUILD_TESTS OFF CACHE BOOL "")
|
||||
set(GLFW_INSTALL OFF CACHE BOOL "")
|
||||
set(GLFW_VULKAN_STATIC OFF CACHE BOOL "")
|
||||
set(GLFW_BUILD_WAYLAND OFF CACHE BOOL "")
|
||||
# Disable ARC for glfw and re-enable after it because it's globally set in the root CMakeLists.txt
|
||||
set(CMAKE_OBJC_FLAGS "")
|
||||
add_subdirectory(glfw)
|
||||
set_target_properties(glfw PROPERTIES UNITY_BUILD OFF)
|
||||
set_target_properties(glfw PROPERTIES XCODE_ATTRIBUTE_CLANG_ENABLE_OBJC_ARC NO)
|
||||
set(CMAKE_OBJC_FLAGS -fobjc-arc)
|
||||
|
||||
add_subdirectory(imgui)
|
||||
endif()
|
||||
|
|
1
3party/glfw
Submodule
1
3party/glfw
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 21fea01161e0d6b70c0c5c1f52dc8e7a7df14a50
|
16
3party/imgui/CMakeLists.txt
Normal file
16
3party/imgui/CMakeLists.txt
Normal file
|
@ -0,0 +1,16 @@
|
|||
project(imgui)
|
||||
|
||||
set(SRC
|
||||
imgui/imgui_draw.cpp
|
||||
imgui/imgui_tables.cpp
|
||||
imgui/imgui_widgets.cpp
|
||||
imgui/imgui.cpp
|
||||
imgui/backends/imgui_impl_glfw.cpp
|
||||
)
|
||||
|
||||
add_library(${PROJECT_NAME} ${SRC})
|
||||
target_include_directories(${PROJECT_NAME}
|
||||
PRIVATE ${OMIM_ROOT}/3party/glfw/include
|
||||
PUBLIC ${OMIM_ROOT}/3party/imgui/imgui
|
||||
PUBLIC .
|
||||
)
|
1
3party/imgui/imgui
Submodule
1
3party/imgui/imgui
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 6982ce43f5b143c5dce5fab0ce07dd4867b705ae
|
|
@ -90,6 +90,19 @@ else()
|
|||
message(FATAL_ERROR "Unsupported platform: ${CMAKE_SYSTEM_NAME}")
|
||||
endif()
|
||||
|
||||
if(${PLATFORM_MAC})
|
||||
set(XCODE_ATTRIBUTE_CLANG_ENABLE_OBJC_ARC YES)
|
||||
|
||||
# Metal language support
|
||||
list(APPEND CMAKE_MODULE_PATH ${OMIM_ROOT}/3party/CMake-MetalShaderSupport/cmake)
|
||||
include(CheckLanguage)
|
||||
include(MetalShaderSupport)
|
||||
check_language(Metal)
|
||||
if(CMAKE_Metal_COMPILER)
|
||||
enable_language(Metal)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Sanitizer
|
||||
if (PLATFORM_DESKTOP)
|
||||
# https://clang.llvm.org/docs/UsersManual.html#controlling-code-generation
|
||||
|
@ -265,16 +278,6 @@ find_package(Threads REQUIRED)
|
|||
|
||||
# Scripts
|
||||
|
||||
if (NOT CMAKE_HOST_WIN32)
|
||||
execute_process(
|
||||
COMMAND "${OMIM_ROOT}/tools/unix/check_cert.sh"
|
||||
RESULT_VARIABLE CheckCertResult
|
||||
)
|
||||
if (CheckCertResult)
|
||||
message(FATAL_ERROR "Certificate check failed")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (NOT PLATFORM_IPHONE AND NOT PLATFORM_ANDROID)
|
||||
list(APPEND qt_components Core Network)
|
||||
if (NOT SKIP_QT_GUI OR NOT SKIP_TESTS OR PYBINDINGS)
|
||||
|
@ -394,6 +397,7 @@ if (PLATFORM_DESKTOP)
|
|||
add_subdirectory(qt)
|
||||
omim_add_tool_subdirectory(skin_generator)
|
||||
endif()
|
||||
add_subdirectory(dev_sandbox)
|
||||
endif()
|
||||
|
||||
omim_add_test_subdirectory(qt_tstfrm)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<div align="center">
|
||||
<img src="/qt/res/logo.png" height="100"/>
|
||||
<img src="qt/res/logo.png" height="100"/>
|
||||
</div>
|
||||
<h1 align="center"">Organic Maps</h1>
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ buildscript {
|
|||
googleFirebaseServicesDefault
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:8.7.2'
|
||||
classpath 'com.android.tools.build:gradle:8.7.3'
|
||||
|
||||
if (googleFirebaseServicesEnabled) {
|
||||
println('Building with Google Firebase Services')
|
||||
|
@ -100,7 +100,7 @@ android {
|
|||
// All properties are read from gradle.properties file
|
||||
compileSdk propCompileSdkVersion.toInteger()
|
||||
|
||||
ndkVersion '27.1.12297006'
|
||||
ndkVersion '27.2.12479018'
|
||||
|
||||
defaultConfig {
|
||||
// Default package name is taken from the manifest and should be app.organicmaps
|
||||
|
@ -363,7 +363,7 @@ android {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.3'
|
||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.4'
|
||||
|
||||
// Google Play Location Services
|
||||
//
|
||||
|
@ -379,7 +379,7 @@ dependencies {
|
|||
huaweiImplementation 'com.google.android.gms:play-services-location:21.3.0'
|
||||
// This is the microG project's re-implementation which is permissible on
|
||||
// F-droid because it's Apache-2.0.
|
||||
fdroidImplementation 'org.microg.gms:play-services-location:0.3.4.240913'
|
||||
fdroidImplementation 'org.microg.gms:play-services-location:0.3.6.244735'
|
||||
|
||||
// Google Firebase Services
|
||||
if (googleFirebaseServicesEnabled) {
|
||||
|
@ -396,11 +396,11 @@ dependencies {
|
|||
// We don't use Kotlin, but some dependencies are actively using it.
|
||||
// See https://stackoverflow.com/a/75719642
|
||||
implementation 'androidx.core:core:1.15.0'
|
||||
implementation(platform('org.jetbrains.kotlin:kotlin-bom:2.0.21'))
|
||||
implementation(platform('org.jetbrains.kotlin:kotlin-bom:2.1.10'))
|
||||
implementation 'androidx.annotation:annotation:1.9.1'
|
||||
implementation 'androidx.appcompat:appcompat:1.7.0'
|
||||
implementation 'androidx.car.app:app:1.7.0-beta03'
|
||||
implementation 'androidx.car.app:app-projected:1.7.0-beta03'
|
||||
implementation 'androidx.car.app:app:1.7.0-rc01'
|
||||
implementation 'androidx.car.app:app-projected:1.7.0-rc01'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.2.0'
|
||||
implementation 'androidx.fragment:fragment:1.8.5'
|
||||
implementation 'androidx.preference:preference:1.2.1'
|
||||
|
@ -417,7 +417,7 @@ dependencies {
|
|||
// Test Dependencies
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.2.1'
|
||||
testImplementation 'junit:junit:4.13.2'
|
||||
testImplementation 'org.mockito:mockito-core:5.12.0'
|
||||
testImplementation 'org.mockito:mockito-core:5.15.2'
|
||||
testImplementation 'org.mockito:mockito-inline:5.2.0'
|
||||
}
|
||||
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION"/>
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC"/>
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE"/>
|
||||
<!--
|
||||
Android 13 (API level 33) and higher supports a runtime permission for sending non-exempt (including Foreground
|
||||
|
@ -86,6 +87,7 @@
|
|||
<activity
|
||||
android:name="app.organicmaps.SplashActivity"
|
||||
android:configChanges="orientation|screenSize|smallestScreenSize|density|screenLayout|uiMode|keyboard|keyboardHidden|navigation"
|
||||
android:screenOrientation="fullUser"
|
||||
android:exported="true">
|
||||
|
||||
<intent-filter>
|
||||
|
@ -120,7 +122,7 @@
|
|||
<data android:scheme="https"/>
|
||||
<data android:host="omaps.app"/>
|
||||
</intent-filter>
|
||||
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
|
@ -344,7 +346,8 @@
|
|||
|
||||
<activity
|
||||
android:name="app.organicmaps.DownloadResourcesLegacyActivity"
|
||||
android:configChanges="orientation|screenLayout|screenSize"/>
|
||||
android:configChanges="orientation|screenLayout|screenSize"
|
||||
android:screenOrientation="fullUser"/>
|
||||
|
||||
<activity-alias
|
||||
android:name="app.organicmaps.DownloadResourcesActivity"
|
||||
|
@ -361,6 +364,7 @@
|
|||
android:name="app.organicmaps.MwmActivity"
|
||||
android:launchMode="singleTask"
|
||||
android:configChanges="uiMode"
|
||||
android:screenOrientation="fullUser"
|
||||
android:windowSoftInputMode="stateAlwaysHidden|adjustPan"/>
|
||||
|
||||
<activity-alias
|
||||
|
@ -371,6 +375,7 @@
|
|||
<activity
|
||||
android:name="app.organicmaps.downloader.DownloaderActivity"
|
||||
android:configChanges="orientation|screenLayout|screenSize"
|
||||
android:screenOrientation="fullUser"
|
||||
android:label="@string/download_maps"
|
||||
android:parentActivityName="app.organicmaps.MwmActivity"
|
||||
android:windowSoftInputMode="adjustResize" />
|
||||
|
@ -378,6 +383,7 @@
|
|||
<activity
|
||||
android:name="app.organicmaps.search.SearchActivity"
|
||||
android:configChanges="orientation|screenLayout|screenSize"
|
||||
android:screenOrientation="fullUser"
|
||||
android:label="@string/search_map"
|
||||
android:parentActivityName="app.organicmaps.MwmActivity"
|
||||
android:windowSoftInputMode="stateVisible|adjustResize" />
|
||||
|
@ -385,6 +391,7 @@
|
|||
<activity
|
||||
android:name="app.organicmaps.settings.SettingsActivity"
|
||||
android:configChanges="orientation|screenLayout|screenSize"
|
||||
android:screenOrientation="fullUser"
|
||||
android:label="@string/settings"
|
||||
android:parentActivityName="app.organicmaps.MwmActivity" />
|
||||
|
||||
|
@ -402,6 +409,7 @@
|
|||
<activity
|
||||
android:name="app.organicmaps.bookmarks.BookmarkCategoriesActivity"
|
||||
android:configChanges="orientation|screenLayout|screenSize"
|
||||
android:screenOrientation="fullUser"
|
||||
android:label="@string/bookmarks_and_tracks"
|
||||
android:parentActivityName="app.organicmaps.MwmActivity"
|
||||
android:windowSoftInputMode="adjustResize" />
|
||||
|
@ -409,6 +417,7 @@
|
|||
<activity
|
||||
android:name="app.organicmaps.bookmarks.BookmarkListActivity"
|
||||
android:configChanges="orientation|screenLayout|screenSize"
|
||||
android:screenOrientation="fullUser"
|
||||
android:label="@string/bookmarks"
|
||||
android:parentActivityName="app.organicmaps.bookmarks.BookmarkCategoriesActivity"
|
||||
android:windowSoftInputMode="adjustResize" />
|
||||
|
@ -416,6 +425,7 @@
|
|||
<activity
|
||||
android:name="app.organicmaps.editor.EditorActivity"
|
||||
android:configChanges="orientation|screenLayout|screenSize"
|
||||
android:screenOrientation="fullUser"
|
||||
android:label="@string/edit_place"
|
||||
android:parentActivityName="app.organicmaps.MwmActivity"
|
||||
android:windowSoftInputMode="adjustResize" />
|
||||
|
@ -454,14 +464,16 @@
|
|||
<service
|
||||
android:name="app.organicmaps.car.CarAppService"
|
||||
android:exported="true"
|
||||
android:foregroundServiceType="specialUse"
|
||||
tools:ignore="ExportedService">
|
||||
<intent-filter>
|
||||
<action android:name="androidx.car.app.CarAppService" />
|
||||
<category android:name="androidx.car.app.category.NAVIGATION" />
|
||||
</intent-filter>
|
||||
<property android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
|
||||
android:value="@string/android_auto_fgs_explanation_for_special_use"/>
|
||||
<intent-filter>
|
||||
<action android:name="androidx.car.app.action.NAVIGATE" />
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<data android:scheme="geo" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<service
|
||||
|
@ -478,6 +490,13 @@
|
|||
android:stopWithTask="false"
|
||||
/>
|
||||
|
||||
<service
|
||||
android:name=".downloader.DownloaderService"
|
||||
android:foregroundServiceType="dataSync"
|
||||
android:exported="false"
|
||||
android:enabled="true"
|
||||
android:stopWithTask="false"/>
|
||||
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="${FILE_PROVIDER_PLACEHOLDER}"
|
||||
|
@ -493,8 +512,8 @@
|
|||
<meta-data android:name="android.webkit.WebView.EnableSafeBrowsing" android:value="false" />
|
||||
<!-- Disable Google's anonymous stats collection -->
|
||||
<meta-data android:name="android.webkit.WebView.MetricsOptOut" android:value="true" />
|
||||
|
||||
<!-- Version >= 3.0. Dex Dual Mode support for compatible Samsung devices.
|
||||
|
||||
<!-- Version >= 3.0. Dex Dual Mode support for compatible Samsung devices.
|
||||
See the documentation: https://developer.samsung.com/samsung-dex/modify-optimizing.html //-->
|
||||
<meta-data android:name="com.samsung.android.multidisplay.keep_process_alive" android:value="true" />
|
||||
|
||||
|
|
|
@ -22,11 +22,13 @@ set(SRC
|
|||
app/organicmaps/vulkan/android_vulkan_context_factory.hpp
|
||||
|
||||
# JNI sources
|
||||
app/organicmaps/sdk/search/DisplayedCategories.cpp
|
||||
app/organicmaps/sdk/search/SearchEngine.cpp
|
||||
app/organicmaps/sdk/search/SearchRecents.cpp
|
||||
app/organicmaps/core/jni_helper.cpp
|
||||
app/organicmaps/core/jni_java_methods.cpp
|
||||
app/organicmaps/core/logging.cpp
|
||||
app/organicmaps/bookmarks/data/BookmarkManager.cpp
|
||||
app/organicmaps/DisplayedCategories.cpp
|
||||
app/organicmaps/DownloadResourcesLegacyActivity.cpp
|
||||
app/organicmaps/editor/Editor.cpp
|
||||
app/organicmaps/editor/OpeningHours.cpp
|
||||
|
@ -38,8 +40,6 @@ set(SRC
|
|||
app/organicmaps/MapManager.cpp
|
||||
app/organicmaps/MwmApplication.cpp
|
||||
app/organicmaps/routing/RoutingOptions.cpp
|
||||
app/organicmaps/SearchEngine.cpp
|
||||
app/organicmaps/SearchRecents.cpp
|
||||
app/organicmaps/settings/UnitLocale.cpp
|
||||
app/organicmaps/settings/MapLanguageCode.cpp
|
||||
app/organicmaps/sound/tts.cpp
|
||||
|
|
|
@ -1578,6 +1578,12 @@ Java_app_organicmaps_Framework_nativeAddRoutePoint(JNIEnv * env, jclass, jstring
|
|||
frm()->GetRoutingManager().AddRoutePoint(std::move(data));
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_app_organicmaps_Framework_nativeRemoveRoutePoints(JNIEnv * env, jclass)
|
||||
{
|
||||
frm()->GetRoutingManager().RemoveRoutePoints();
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_app_organicmaps_Framework_nativeRemoveRoutePoint(JNIEnv * env, jclass,
|
||||
jint markType, jint intermediateIndex)
|
||||
|
@ -1627,6 +1633,13 @@ Java_app_organicmaps_Framework_nativeGetRoutePoints(JNIEnv * env, jclass)
|
|||
});
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_app_organicmaps_Framework_nativeMoveRoutePoint(JNIEnv * env, jclass,
|
||||
jint currentIndex, jint targetIndex)
|
||||
{
|
||||
frm()->GetRoutingManager().MoveRoutePoint(currentIndex, targetIndex);
|
||||
}
|
||||
|
||||
JNIEXPORT jobject JNICALL
|
||||
Java_app_organicmaps_Framework_nativeGetTransitRouteInfo(JNIEnv * env, jclass)
|
||||
{
|
||||
|
|
|
@ -1,40 +0,0 @@
|
|||
#include "Framework.hpp"
|
||||
|
||||
#include "search/result.hpp"
|
||||
|
||||
#include "app/organicmaps/core/jni_helper.hpp"
|
||||
#include "app/organicmaps/core/jni_java_methods.hpp"
|
||||
|
||||
using SearchRequest = search::QuerySaver::SearchRequest;
|
||||
|
||||
extern "C"
|
||||
{
|
||||
JNIEXPORT void JNICALL
|
||||
Java_app_organicmaps_search_SearchRecents_nativeGetList(JNIEnv * env, jclass thiz, jobject result)
|
||||
{
|
||||
auto const & items = g_framework->NativeFramework()->GetSearchAPI().GetLastSearchQueries();
|
||||
if (items.empty())
|
||||
return;
|
||||
|
||||
auto const listAddMethod = jni::ListBuilder::Instance(env).m_add;
|
||||
|
||||
for (SearchRequest const & item : items)
|
||||
{
|
||||
jni::TScopedLocalRef str(env, jni::ToJavaString(env, item.second));
|
||||
env->CallBooleanMethod(result, listAddMethod, str.get());
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_app_organicmaps_search_SearchRecents_nativeAdd(JNIEnv * env, jclass thiz, jstring locale, jstring query)
|
||||
{
|
||||
SearchRequest const sr(jni::ToNativeString(env, locale), jni::ToNativeString(env, query));
|
||||
g_framework->NativeFramework()->GetSearchAPI().SaveSearchQuery(sr);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_app_organicmaps_search_SearchRecents_nativeClear(JNIEnv * env, jclass thiz)
|
||||
{
|
||||
g_framework->NativeFramework()->GetSearchAPI().ClearSearchHistory();
|
||||
}
|
||||
}
|
|
@ -28,7 +28,7 @@ void InjectMetadata(JNIEnv * env, jclass const clazz, jobject const mapObject, o
|
|||
//jobject CreatePopularity(JNIEnv * env, place_page::Info const & info)
|
||||
//{
|
||||
// static jclass const popularityClass =
|
||||
// jni::GetGlobalClassRef(env, "app/organicmaps/search/Popularity");
|
||||
// jni::GetGlobalClassRef(env, "app/organicmaps/sdk/search/Popularity");
|
||||
// static jmethodID const popularityConstructor =
|
||||
// jni::GetConstructorID(env, popularityClass, "(I)V");
|
||||
// auto const popularityValue = info.GetPopularity();
|
||||
|
@ -57,7 +57,7 @@ jobject CreateMapObject(JNIEnv * env, place_page::Info const & info, int mapObje
|
|||
"Ljava/lang/String;" // appId
|
||||
"Lapp/organicmaps/routing/RoutePointInfo;" // routePointInfo
|
||||
"I" // openingMode
|
||||
"Lapp/organicmaps/search/Popularity;" // popularity
|
||||
"Lapp/organicmaps/sdk/search/Popularity;" // popularity
|
||||
"Ljava/lang/String;" // description
|
||||
"I" // roadWarnType
|
||||
"[Ljava/lang/String;" // rawTypes
|
||||
|
@ -105,7 +105,7 @@ jobject CreateBookmark(JNIEnv *env, const place_page::Info &info,
|
|||
"(Lapp/organicmaps/bookmarks/data/FeatureId;JJLjava/lang/String;"
|
||||
"Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;"
|
||||
"Lapp/organicmaps/routing/RoutePointInfo;"
|
||||
"ILapp/organicmaps/search/Popularity;Ljava/lang/String;"
|
||||
"ILapp/organicmaps/sdk/search/Popularity;Ljava/lang/String;"
|
||||
"[Ljava/lang/String;)V");
|
||||
static jmethodID const featureCtorId =
|
||||
jni::GetConstructorID(env, g_featureIdClazz, "(Ljava/lang/String;JI)V");
|
||||
|
@ -165,12 +165,13 @@ jobject CreateElevationInfo(JNIEnv * env, ElevationInfo const & info)
|
|||
"[Lapp/organicmaps/bookmarks/data/ElevationInfo$Point;"
|
||||
"IIIIIJ)V");
|
||||
jni::TScopedLocalObjectArrayRef jPoints(env, ToElevationPointArray(env, info.GetPoints()));
|
||||
// TODO (KK): elevation info should have only the elevation data - see the https://github.com/organicmaps/organicmaps/pull/10063
|
||||
return env->NewObject(g_elevationInfoClazz, ctorId,
|
||||
jPoints.get(),
|
||||
static_cast<jint>(info.GetAscent()),
|
||||
static_cast<jint>(info.GetDescent()),
|
||||
static_cast<jint>(info.GetMinAltitude()),
|
||||
static_cast<jint>(info.GetMaxAltitude()),
|
||||
// static_cast<jint>(info.GetAscent()),
|
||||
// static_cast<jint>(info.GetDescent()),
|
||||
// static_cast<jint>(info.GetMinAltitude()),
|
||||
// static_cast<jint>(info.GetMaxAltitude()),
|
||||
static_cast<jint>(info.GetDifficulty()));
|
||||
}
|
||||
|
||||
|
|
|
@ -591,6 +591,15 @@ Java_app_organicmaps_bookmarks_data_BookmarkManager_nativeSetAllCategoriesVisibi
|
|||
frm()->GetBookmarkManager().SetAllCategoriesVisibility(static_cast<bool>(visible));
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_app_organicmaps_bookmarks_data_BookmarkManager_nativePrepareTrackFileForSharing(JNIEnv * env, jclass, jlong trackId, jint kmlFileType)
|
||||
{
|
||||
frm()->GetBookmarkManager().PrepareTrackFileForSharing(static_cast<kml::TrackId>(trackId), [env](BookmarkManager::SharingResult const & result)
|
||||
{
|
||||
OnPreparedFileForSharing(env, result);
|
||||
}, static_cast<KmlFileType>(kmlFileType));
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_app_organicmaps_bookmarks_data_BookmarkManager_nativePrepareFileForSharing(JNIEnv * env, jclass, jlongArray catIds, jint kmlFileType)
|
||||
{
|
||||
|
|
|
@ -149,7 +149,8 @@ Java_app_organicmaps_editor_Editor_nativeHasWifi(JNIEnv *, jclass)
|
|||
JNIEXPORT void JNICALL
|
||||
Java_app_organicmaps_editor_Editor_nativeSetHasWifi(JNIEnv *, jclass, jboolean hasWifi)
|
||||
{
|
||||
g_editableMapObject.SetInternet(hasWifi ? feature::Internet::Wlan : feature::Internet::Unknown);
|
||||
if (hasWifi != (g_editableMapObject.GetInternet() == feature::Internet::Wlan))
|
||||
g_editableMapObject.SetInternet(hasWifi ? feature::Internet::Wlan : feature::Internet::Unknown);
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL
|
||||
|
@ -362,7 +363,11 @@ Java_app_organicmaps_editor_Editor_nativeStartEdit(JNIEnv *, jclass)
|
|||
{
|
||||
::Framework * frm = g_framework->NativeFramework();
|
||||
if (!frm->HasPlacePageInfo())
|
||||
{
|
||||
ASSERT(g_editableMapObject.GetEditingLifecycle() == osm::EditingLifecycle::CREATED,
|
||||
("PlacePageInfo should only be empty for new features."));
|
||||
return;
|
||||
}
|
||||
|
||||
place_page::Info const & info = g_framework->GetPlacePageInfo();
|
||||
CHECK(frm->GetEditableMapObject(info.GetID(), g_editableMapObject), ("Invalid feature in the place page."));
|
||||
|
|
|
@ -5,8 +5,7 @@
|
|||
|
||||
extern "C"
|
||||
{
|
||||
JNIEXPORT jobjectArray JNICALL
|
||||
Java_app_organicmaps_search_DisplayedCategories_nativeGetKeys(JNIEnv * env, jclass clazz)
|
||||
JNIEXPORT jobjectArray JNICALL Java_app_organicmaps_sdk_search_DisplayedCategories_nativeGetKeys(JNIEnv * env, jclass)
|
||||
{
|
||||
::Framework * fr = g_framework->NativeFramework();
|
||||
ASSERT(fr, ());
|
|
@ -39,7 +39,7 @@ Results g_results;
|
|||
|
||||
// Timestamp of last search query. Results with older stamps are ignored.
|
||||
jlong g_queryTimestamp;
|
||||
// Implements 'NativeSearchListener' java interface.
|
||||
// Implements 'SearchListener' java interface.
|
||||
jobject g_javaListener;
|
||||
jmethodID g_updateResultsId;
|
||||
jmethodID g_endResultsId;
|
||||
|
@ -52,7 +52,7 @@ jmethodID g_descriptionConstructor;
|
|||
jclass g_popularityClass;
|
||||
jmethodID g_popularityConstructor;
|
||||
|
||||
// Implements 'NativeMapSearchListener' java interface.
|
||||
// Implements 'MapSearchListener' java interface.
|
||||
jmethodID g_mapResultsMethod;
|
||||
jclass g_mapResultClass;
|
||||
jmethodID g_mapResultCtor;
|
||||
|
@ -232,21 +232,21 @@ void OnBookmarksSearchResults(search::BookmarksSearchParams::Results results,
|
|||
extern "C"
|
||||
{
|
||||
JNIEXPORT void JNICALL
|
||||
Java_app_organicmaps_search_SearchEngine_nativeInit(JNIEnv * env, jobject thiz)
|
||||
Java_app_organicmaps_sdk_search_SearchEngine_nativeInit(JNIEnv * env, jobject thiz)
|
||||
{
|
||||
g_javaListener = env->NewGlobalRef(thiz);
|
||||
// public void onResultsUpdate(@NonNull SearchResult[] results, long timestamp)
|
||||
g_updateResultsId = jni::GetMethodID(env, g_javaListener, "onResultsUpdate",
|
||||
"([Lapp/organicmaps/search/SearchResult;J)V");
|
||||
"([Lapp/organicmaps/sdk/search/SearchResult;J)V");
|
||||
// public void onResultsEnd(long timestamp)
|
||||
g_endResultsId = jni::GetMethodID(env, g_javaListener, "onResultsEnd", "(J)V");
|
||||
g_resultClass = jni::GetGlobalClassRef(env, "app/organicmaps/search/SearchResult");
|
||||
g_resultClass = jni::GetGlobalClassRef(env, "app/organicmaps/sdk/search/SearchResult");
|
||||
g_resultConstructor = jni::GetConstructorID(
|
||||
env, g_resultClass,
|
||||
"(Ljava/lang/String;Lapp/organicmaps/search/SearchResult$Description;DD[I[I"
|
||||
"Lapp/organicmaps/search/Popularity;)V");
|
||||
"(Ljava/lang/String;Lapp/organicmaps/sdk/search/SearchResult$Description;DD[I[I"
|
||||
"Lapp/organicmaps/sdk/search/Popularity;)V");
|
||||
g_suggestConstructor = jni::GetConstructorID(env, g_resultClass, "(Ljava/lang/String;Ljava/lang/String;DD[I[I)V");
|
||||
g_descriptionClass = jni::GetGlobalClassRef(env, "app/organicmaps/search/SearchResult$Description");
|
||||
g_descriptionClass = jni::GetGlobalClassRef(env, "app/organicmaps/sdk/search/SearchResult$Description");
|
||||
/*
|
||||
Description(FeatureId featureId, String featureType, String region, Distance distance,
|
||||
String description, int openNow, int minutesUntilOpen, int minutesUntilClosed,
|
||||
|
@ -257,12 +257,12 @@ extern "C"
|
|||
"Ljava/lang/String;Ljava/lang/String;Lapp/organicmaps/util/Distance;"
|
||||
"Ljava/lang/String;IIIZ)V");
|
||||
|
||||
g_popularityClass = jni::GetGlobalClassRef(env, "app/organicmaps/search/Popularity");
|
||||
g_popularityClass = jni::GetGlobalClassRef(env, "app/organicmaps/sdk/search/Popularity");
|
||||
g_popularityConstructor = jni::GetConstructorID(env, g_popularityClass, "(I)V");
|
||||
|
||||
g_mapResultsMethod = jni::GetMethodID(env, g_javaListener, "onMapSearchResults",
|
||||
"([Lapp/organicmaps/search/NativeMapSearchListener$Result;JZ)V");
|
||||
g_mapResultClass = jni::GetGlobalClassRef(env, "app/organicmaps/search/NativeMapSearchListener$Result");
|
||||
"([Lapp/organicmaps/sdk/search/MapSearchListener$Result;JZ)V");
|
||||
g_mapResultClass = jni::GetGlobalClassRef(env, "app/organicmaps/sdk/search/MapSearchListener$Result");
|
||||
g_mapResultCtor = jni::GetConstructorID(env, g_mapResultClass, "(Ljava/lang/String;Ljava/lang/String;)V");
|
||||
|
||||
g_updateBookmarksResultsId =
|
||||
|
@ -271,7 +271,7 @@ extern "C"
|
|||
jni::GetMethodID(env, g_javaListener, "onBookmarkSearchResultsEnd", "([JJ)V");
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL Java_app_organicmaps_search_SearchEngine_nativeRunSearch(
|
||||
JNIEXPORT jboolean JNICALL Java_app_organicmaps_sdk_search_SearchEngine_nativeRunSearch(
|
||||
JNIEnv * env, jclass clazz, jbyteArray bytes, jboolean isCategory,
|
||||
jstring lang, jlong timestamp, jboolean hasPosition, jdouble lat, jdouble lon)
|
||||
{
|
||||
|
@ -288,7 +288,7 @@ extern "C"
|
|||
return searchStarted;
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_app_organicmaps_search_SearchEngine_nativeRunInteractiveSearch(
|
||||
JNIEXPORT void JNICALL Java_app_organicmaps_sdk_search_SearchEngine_nativeRunInteractiveSearch(
|
||||
JNIEnv * env, jclass clazz, jbyteArray bytes, jboolean isCategory,
|
||||
jstring lang, jlong timestamp, jboolean isMapAndTable, jboolean hasPosition, jdouble lat, jdouble lon)
|
||||
{
|
||||
|
@ -321,7 +321,7 @@ extern "C"
|
|||
}
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_app_organicmaps_search_SearchEngine_nativeRunSearchMaps(
|
||||
JNIEXPORT void JNICALL Java_app_organicmaps_sdk_search_SearchEngine_nativeRunSearchMaps(
|
||||
JNIEnv * env, jclass clazz, jbyteArray bytes, jstring lang, jlong timestamp)
|
||||
{
|
||||
storage::DownloaderSearchParams params{
|
||||
|
@ -334,7 +334,7 @@ extern "C"
|
|||
g_queryTimestamp = timestamp;
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL Java_app_organicmaps_search_SearchEngine_nativeRunSearchInBookmarks(
|
||||
JNIEXPORT jboolean JNICALL Java_app_organicmaps_sdk_search_SearchEngine_nativeRunSearchInBookmarks(
|
||||
JNIEnv * env, jclass clazz, jbyteArray query, jlong catId, jlong timestamp)
|
||||
{
|
||||
search::BookmarksSearchParams params{
|
||||
|
@ -350,25 +350,25 @@ extern "C"
|
|||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_app_organicmaps_search_SearchEngine_nativeShowResult(JNIEnv * env, jclass clazz, jint index)
|
||||
Java_app_organicmaps_sdk_search_SearchEngine_nativeShowResult(JNIEnv * env, jclass clazz, jint index)
|
||||
{
|
||||
g_framework->NativeFramework()->ShowSearchResult(g_results[index]);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_app_organicmaps_search_SearchEngine_nativeCancelInteractiveSearch(JNIEnv * env, jclass clazz)
|
||||
Java_app_organicmaps_sdk_search_SearchEngine_nativeCancelInteractiveSearch(JNIEnv * env, jclass clazz)
|
||||
{
|
||||
g_framework->NativeFramework()->GetSearchAPI().CancelSearch(search::Mode::Viewport);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_app_organicmaps_search_SearchEngine_nativeCancelEverywhereSearch(JNIEnv * env, jclass clazz)
|
||||
Java_app_organicmaps_sdk_search_SearchEngine_nativeCancelEverywhereSearch(JNIEnv * env, jclass clazz)
|
||||
{
|
||||
g_framework->NativeFramework()->GetSearchAPI().CancelSearch(search::Mode::Everywhere);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_app_organicmaps_search_SearchEngine_nativeCancelAllSearches(JNIEnv * env, jclass clazz)
|
||||
Java_app_organicmaps_sdk_search_SearchEngine_nativeCancelAllSearches(JNIEnv * env, jclass clazz)
|
||||
{
|
||||
g_framework->NativeFramework()->GetSearchAPI().CancelAllSearches();
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
#include "app/organicmaps/Framework.hpp"
|
||||
#include "app/organicmaps/core/jni_helper.hpp"
|
||||
#include "app/organicmaps/core/jni_java_methods.hpp"
|
||||
|
||||
#include "search/result.hpp"
|
||||
|
||||
using SearchRequest = search::QuerySaver::SearchRequest;
|
||||
|
||||
extern "C"
|
||||
{
|
||||
JNIEXPORT void JNICALL Java_app_organicmaps_sdk_search_SearchRecents_nativeGetList(JNIEnv * env, jclass, jobject result)
|
||||
{
|
||||
auto const & items = g_framework->NativeFramework()->GetSearchAPI().GetLastSearchQueries();
|
||||
if (items.empty())
|
||||
return;
|
||||
|
||||
auto const listAddMethod = jni::ListBuilder::Instance(env).m_add;
|
||||
|
||||
for (SearchRequest const & item : items)
|
||||
{
|
||||
jni::TScopedLocalRef str(env, jni::ToJavaString(env, item.second));
|
||||
env->CallBooleanMethod(result, listAddMethod, str.get());
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_app_organicmaps_sdk_search_SearchRecents_nativeAdd(JNIEnv * env, jclass, jstring locale,
|
||||
jstring query)
|
||||
{
|
||||
SearchRequest const sr(jni::ToNativeString(env, locale), jni::ToNativeString(env, query));
|
||||
g_framework->NativeFramework()->GetSearchAPI().SaveSearchQuery(sr);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_app_organicmaps_sdk_search_SearchRecents_nativeClear(JNIEnv * env, jclass)
|
||||
{
|
||||
g_framework->NativeFramework()->GetSearchAPI().ClearSearchHistory();
|
||||
}
|
||||
}
|
|
@ -49,6 +49,12 @@ Java_app_organicmaps_util_StringUtils_nativeFilterContainsNormalized(JNIEnv * en
|
|||
return jni::ToJavaStringArray(env, filtered);
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL Java_app_organicmaps_util_StringUtils_nativeFormatSpeed(
|
||||
JNIEnv * env, jclass thiz, jdouble metersPerSecond)
|
||||
{
|
||||
return measurement_utils::FormatSpeed(metersPerSecond, measurement_utils::GetMeasurementUnits());
|
||||
}
|
||||
|
||||
JNIEXPORT jobject JNICALL Java_app_organicmaps_util_StringUtils_nativeFormatSpeedAndUnits(
|
||||
JNIEnv * env, jclass thiz, jdouble metersPerSecond)
|
||||
{
|
||||
|
|
|
@ -390,7 +390,7 @@ public class DownloadResourcesLegacyActivity extends BaseMwmFragmentActivity
|
|||
mProgress.setProgressCompat(0, true);
|
||||
|
||||
mCountryDownloadListenerSlot = MapManager.nativeSubscribe(mCountryDownloadListener);
|
||||
MapManager.nativeDownload(mCurrentCountry);
|
||||
MapManager.startDownload(mCurrentCountry);
|
||||
setAction(PROCEED_TO_MAP);
|
||||
}
|
||||
else
|
||||
|
|
|
@ -333,11 +333,20 @@ public class Framework
|
|||
public static native int nativeGetBestRouter(double srcLat, double srcLon,
|
||||
double dstLat, double dstLon);
|
||||
|
||||
public static void addRoutePoint(RouteMarkData point)
|
||||
{
|
||||
Framework.nativeAddRoutePoint(point.mTitle, point.mSubtitle, point.mPointType,
|
||||
point.mIntermediateIndex, point.mIsMyPosition,
|
||||
point.mLat, point.mLon);
|
||||
}
|
||||
|
||||
public static native void nativeAddRoutePoint(String title, String subtitle,
|
||||
@RoutePointInfo.RouteMarkType int markType,
|
||||
int intermediateIndex, boolean isMyPosition,
|
||||
double lat, double lon);
|
||||
|
||||
public static native void nativeRemoveRoutePoints();
|
||||
|
||||
public static native void nativeRemoveRoutePoint(@RoutePointInfo.RouteMarkType int markType,
|
||||
int intermediateIndex);
|
||||
|
||||
|
@ -346,6 +355,9 @@ public class Framework
|
|||
public static native boolean nativeCouldAddIntermediatePoint();
|
||||
@NonNull
|
||||
public static native RouteMarkData[] nativeGetRoutePoints();
|
||||
|
||||
public static native void nativeMoveRoutePoint(int currentIndex, int targetIndex);
|
||||
|
||||
@NonNull
|
||||
public static native TransitRouteInfo nativeGetTransitRouteInfo();
|
||||
/**
|
||||
|
|
|
@ -77,7 +77,7 @@ import app.organicmaps.maplayer.MapButtonsViewModel;
|
|||
import app.organicmaps.maplayer.ToggleMapLayerFragment;
|
||||
import app.organicmaps.maplayer.isolines.IsolinesManager;
|
||||
import app.organicmaps.maplayer.isolines.IsolinesState;
|
||||
import app.organicmaps.maplayer.subway.SubwayManager;
|
||||
import app.organicmaps.routing.ManageRouteBottomSheet;
|
||||
import app.organicmaps.routing.NavigationController;
|
||||
import app.organicmaps.routing.NavigationService;
|
||||
import app.organicmaps.routing.RoutePointInfo;
|
||||
|
@ -89,7 +89,7 @@ import app.organicmaps.routing.RoutingPlanFragment;
|
|||
import app.organicmaps.routing.RoutingPlanInplaceController;
|
||||
import app.organicmaps.search.FloatingSearchToolbarController;
|
||||
import app.organicmaps.search.SearchActivity;
|
||||
import app.organicmaps.search.SearchEngine;
|
||||
import app.organicmaps.sdk.search.SearchEngine;
|
||||
import app.organicmaps.search.SearchFragment;
|
||||
import app.organicmaps.settings.DrivingOptionsActivity;
|
||||
import app.organicmaps.settings.RoadType;
|
||||
|
@ -150,6 +150,7 @@ public class MwmActivity extends BaseMwmFragmentActivity
|
|||
public static final String EXTRA_TRACK_ID = "track_id";
|
||||
public static final String EXTRA_UPDATE_THEME = "update_theme";
|
||||
private static final String EXTRA_CONSUMED = "mwm.extra.intent.processed";
|
||||
private boolean mPreciseLocationDialogShown = false;
|
||||
|
||||
private static final String[] DOCKED_FRAGMENTS = { SearchFragment.class.getName(),
|
||||
DownloaderFragment.class.getName(),
|
||||
|
@ -157,7 +158,11 @@ public class MwmActivity extends BaseMwmFragmentActivity
|
|||
EditorHostFragment.class.getName(),
|
||||
ReportFragment.class.getName() };
|
||||
|
||||
public static final int REQ_CODE_DRIVING_OPTIONS = 6;
|
||||
public final ActivityResultLauncher<Intent> startDrivingOptionsForResult = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), activityResult ->
|
||||
{
|
||||
if( activityResult.getResultCode() == Activity.RESULT_OK)
|
||||
rebuildLastRoute();
|
||||
});
|
||||
|
||||
private static final String MAIN_MENU_ID = "MAIN_MENU_BOTTOM_SHEET";
|
||||
private static final String LAYERS_MENU_ID = "LAYERS_MENU_BOTTOM_SHEET";
|
||||
|
@ -229,6 +234,8 @@ public class MwmActivity extends BaseMwmFragmentActivity
|
|||
@NonNull
|
||||
private DisplayManager mDisplayManager;
|
||||
|
||||
ManageRouteBottomSheet mManageRouteBottomSheet;
|
||||
|
||||
private boolean mRemoveDisplayListener = true;
|
||||
private int mLastUiMode = Configuration.UI_MODE_TYPE_UNDEFINED;
|
||||
|
||||
|
@ -586,14 +593,14 @@ public class MwmActivity extends BaseMwmFragmentActivity
|
|||
ViewCompat.setOnApplyWindowInsetsListener(mPointChooser, (view, windowInsets) -> {
|
||||
UiUtils.setViewInsetsPaddingBottom(mPointChooser, windowInsets);
|
||||
UiUtils.setViewInsetsPaddingNoBottom(mPointChooserToolbar, windowInsets);
|
||||
|
||||
final int trackRecorderOffset = TrackRecorder.nativeIsTrackRecordingEnabled() ? UiUtils.dimen(this, R.dimen.map_button_size) : 0;
|
||||
mNavBarHeight = isFullscreen() ? 0 : windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()).bottom;
|
||||
// For the first loading, set compass top margin to status bar size
|
||||
// The top inset will be then be updated by the routing controller
|
||||
if (mCurrentWindowInsets == null)
|
||||
updateCompassOffset(windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()).top, windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()).right);
|
||||
else
|
||||
updateCompassOffset(-1, windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()).right);
|
||||
{
|
||||
updateCompassOffset(trackRecorderOffset + windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()).top, windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()).right);
|
||||
}
|
||||
refreshLightStatusBar();
|
||||
updateBottomWidgetsOffset(windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()).left);
|
||||
mCurrentWindowInsets = windowInsets;
|
||||
|
@ -614,7 +621,7 @@ public class MwmActivity extends BaseMwmFragmentActivity
|
|||
|
||||
if (!mIsTabletLayout)
|
||||
{
|
||||
mRoutingPlanInplaceController = new RoutingPlanInplaceController(this, this, this);
|
||||
mRoutingPlanInplaceController = new RoutingPlanInplaceController(this, startDrivingOptionsForResult, this, this);
|
||||
removeCurrentFragment(false);
|
||||
}
|
||||
|
||||
|
@ -691,6 +698,16 @@ public class MwmActivity extends BaseMwmFragmentActivity
|
|||
}
|
||||
}
|
||||
|
||||
/** Hides/shows UI while keeping state
|
||||
* @param isUiHidden True to hide the UI
|
||||
**/
|
||||
public void hideOrShowUIWithoutClosingPlacePage(boolean isUiHidden)
|
||||
{
|
||||
// Used instead of closeBottomSheet to preserve state and hide instantly
|
||||
UiUtils.showIf(!isUiHidden, findViewById(R.id.place_page_container_fragment));
|
||||
mMapButtonsViewModel.setButtonsHidden(isUiHidden);
|
||||
}
|
||||
|
||||
private void showSearchToolbar()
|
||||
{
|
||||
mSearchController.show();
|
||||
|
@ -800,6 +817,7 @@ public class MwmActivity extends BaseMwmFragmentActivity
|
|||
showBottomSheet(MAIN_MENU_ID);
|
||||
}
|
||||
case help -> showHelp();
|
||||
case trackRecordingStatus -> showTrackSaveDialog();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1018,18 +1036,6 @@ public class MwmActivity extends BaseMwmFragmentActivity
|
|||
mPowerSaveDisclaimerShown = savedInstanceState.getBoolean(POWER_SAVE_DISCLAIMER_SHOWN, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data)
|
||||
{
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
|
||||
if (resultCode != Activity.RESULT_OK)
|
||||
return;
|
||||
|
||||
if (requestCode == REQ_CODE_DRIVING_OPTIONS)
|
||||
rebuildLastRoute();
|
||||
}
|
||||
|
||||
private void rebuildLastRoute()
|
||||
{
|
||||
RoutingController.get().attach(this);
|
||||
|
@ -1311,6 +1317,16 @@ public class MwmActivity extends BaseMwmFragmentActivity
|
|||
Framework.nativeGetChoosePositionMode() == Framework.ChoosePositionMode.NONE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchGenericMotionEvent(MotionEvent event) {
|
||||
if (event.getActionMasked() == MotionEvent.ACTION_SCROLL) {
|
||||
int exponent = event.getAxisValue(MotionEvent.AXIS_VSCROLL) < 0 ? -1 : 1;
|
||||
Map.onScale(Math.pow(1.7f, exponent), event.getX(), event.getY(), true);
|
||||
return true;
|
||||
}
|
||||
return super.onGenericMotionEvent(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouch(View view, MotionEvent event)
|
||||
{
|
||||
|
@ -1488,14 +1504,30 @@ public class MwmActivity extends BaseMwmFragmentActivity
|
|||
if (mCurrentWindowInsets == null) {
|
||||
return;
|
||||
}
|
||||
int offset = mCurrentWindowInsets.getInsets(WindowInsetsCompat.Type.systemBars()).top;
|
||||
int offsetY = mCurrentWindowInsets.getInsets(WindowInsetsCompat.Type.systemBars()).top;
|
||||
int offsetX = mCurrentWindowInsets.getInsets(WindowInsetsCompat.Type.systemBars()).right;
|
||||
if (show && mRoutingPlanInplaceController != null)
|
||||
{
|
||||
final int height = mRoutingPlanInplaceController.calcHeight();
|
||||
if (height != 0)
|
||||
offset = height;
|
||||
offsetY = height;
|
||||
}
|
||||
updateCompassOffset(offset);
|
||||
final int orientation = getResources().getConfiguration().orientation;
|
||||
final boolean isTrackRecordingEnabled = TrackRecorder.nativeIsTrackRecordingEnabled();
|
||||
if (isTrackRecordingEnabled && (orientation != Configuration.ORIENTATION_LANDSCAPE))
|
||||
offsetY += UiUtils.dimen(this, R.dimen.map_button_size);
|
||||
if (orientation == Configuration.ORIENTATION_LANDSCAPE)
|
||||
{
|
||||
if (show)
|
||||
{
|
||||
final boolean isSmallScreen = UiUtils.getDisplayTotalHeight(this) < UiUtils.dimen(this, R.dimen.dp_400);
|
||||
if (!isSmallScreen || TrackRecorder.nativeIsTrackRecordingEnabled())
|
||||
offsetX += UiUtils.dimen(this, R.dimen.map_button_size);
|
||||
}
|
||||
else if (isTrackRecordingEnabled)
|
||||
offsetY += UiUtils.dimen(this, R.dimen.map_button_size);
|
||||
}
|
||||
updateCompassOffset(offsetY, offsetX);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1662,12 +1694,6 @@ public class MwmActivity extends BaseMwmFragmentActivity
|
|||
mRoutingPlanInplaceController.showDrivingOptionView();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSubwayEnabled()
|
||||
{
|
||||
return SubwayManager.from(this).isEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCommonBuildError(int lastResultCode, @NonNull String[] lastMissingMaps)
|
||||
{
|
||||
|
@ -1683,7 +1709,7 @@ public class MwmActivity extends BaseMwmFragmentActivity
|
|||
mAlertDialog = new MaterialAlertDialogBuilder(this, R.style.MwmTheme_AlertDialog)
|
||||
.setTitle(R.string.unable_to_calc_alert_title)
|
||||
.setMessage(R.string.unable_to_calc_alert_subtitle)
|
||||
.setPositiveButton(R.string.settings, (dialog, which) -> DrivingOptionsActivity.start(this))
|
||||
.setPositiveButton(R.string.settings, (dialog, which) -> DrivingOptionsActivity.start(this, startDrivingOptionsForResult))
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.setOnDismissListener(dialog -> mAlertDialog = null)
|
||||
.show();
|
||||
|
@ -1796,7 +1822,7 @@ public class MwmActivity extends BaseMwmFragmentActivity
|
|||
}
|
||||
|
||||
// Check for any location permissions.
|
||||
if (!LocationUtils.checkCoarseLocationPermission(this))
|
||||
if (!LocationUtils.checkLocationPermission(this))
|
||||
{
|
||||
Logger.w(LOCATION_TAG, "Permissions ACCESS_COARSE_LOCATION and ACCESS_FINE_LOCATION are not granted");
|
||||
// Calls onMyPositionModeChanged(NOT_FOLLOW_NO_POSITION).
|
||||
|
@ -1934,12 +1960,50 @@ public class MwmActivity extends BaseMwmFragmentActivity
|
|||
mLocationPermissionRequestedForRecording = false;
|
||||
if (LocationUtils.checkLocationPermission(this))
|
||||
{
|
||||
final boolean hasFineLocationPermission = LocationUtils.checkFineLocationPermission(this);
|
||||
|
||||
if (LocationState.getMode() == LocationState.NOT_FOLLOW_NO_POSITION)
|
||||
LocationState.nativeSwitchToNextMode();
|
||||
|
||||
if (requestedForRecording && LocationUtils.checkFineLocationPermission(this))
|
||||
if (requestedForRecording && hasFineLocationPermission)
|
||||
startTrackRecording();
|
||||
|
||||
if (hasFineLocationPermission)
|
||||
{
|
||||
Logger.i(LOCATION_TAG, "ACCESS_FINE_LOCATION permission granted");
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.w(LOCATION_TAG, "Only ACCESS_COARSE_LOCATION permission granted");
|
||||
if (mLocationErrorDialog != null && mLocationErrorDialog.isShowing())
|
||||
{
|
||||
Logger.w(LOCATION_TAG, "Don't show 'Precise Location denied' dialog because another dialog is in progress");
|
||||
return;
|
||||
}
|
||||
if (!mPreciseLocationDialogShown)
|
||||
{
|
||||
mPreciseLocationDialogShown = true;
|
||||
final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this, R.style.MwmTheme_AlertDialog)
|
||||
.setTitle("⚠ " + getString(R.string.limited_accuracy))
|
||||
.setMessage(R.string.precise_location_is_disabled_long_text)
|
||||
.setNegativeButton(R.string.close, (dialog, which) -> dialog.dismiss())
|
||||
.setCancelable(true)
|
||||
.setOnDismissListener(dialog -> mLocationErrorDialog = null);
|
||||
final Intent intent = Utils.makeSystemLocationSettingIntent(this);
|
||||
if (intent != null)
|
||||
{
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
|
||||
builder.setPositiveButton(R.string.location_settings, (dialog, which) -> startActivity(intent));
|
||||
}
|
||||
mLocationErrorDialog = builder.show();
|
||||
}
|
||||
else
|
||||
{
|
||||
Toast.makeText(this, R.string.precise_location_is_disabled_long_text, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -2090,6 +2154,15 @@ public class MwmActivity extends BaseMwmFragmentActivity
|
|||
RoutingController.get().start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onManageRouteOpen()
|
||||
{
|
||||
// Create and show 'Manage Route' Bottom Sheet panel.
|
||||
mManageRouteBottomSheet = new ManageRouteBottomSheet();
|
||||
mManageRouteBottomSheet.setCancelable(false);
|
||||
mManageRouteBottomSheet.show(getSupportFragmentManager(), "ManageRouteBottomSheet");
|
||||
}
|
||||
|
||||
private boolean requestBatterySaverPermission()
|
||||
{
|
||||
if (!PowerManagment.isSystemPowerSaveMode(this))
|
||||
|
@ -2267,6 +2340,11 @@ public class MwmActivity extends BaseMwmFragmentActivity
|
|||
|
||||
requestPostNotificationsPermission();
|
||||
|
||||
if (mCurrentWindowInsets != null)
|
||||
{
|
||||
final int offset = mCurrentWindowInsets.getInsets(WindowInsetsCompat.Type.systemBars()).top;
|
||||
updateCompassOffset(offset + UiUtils.dimen(this, R.dimen.map_button_size));
|
||||
}
|
||||
Toast.makeText(this, R.string.track_recording, Toast.LENGTH_SHORT).show();
|
||||
TrackRecordingService.startForegroundService(getApplicationContext());
|
||||
mMapButtonsViewModel.setTrackRecorderState(true);
|
||||
|
@ -2275,6 +2353,18 @@ public class MwmActivity extends BaseMwmFragmentActivity
|
|||
|
||||
private void stopTrackRecording()
|
||||
{
|
||||
if (mCurrentWindowInsets != null)
|
||||
{
|
||||
int offsetY = mCurrentWindowInsets.getInsets(WindowInsetsCompat.Type.systemBars()).top;
|
||||
final int offsetX = mCurrentWindowInsets.getInsets(WindowInsetsCompat.Type.systemBars()).right;
|
||||
if (RoutingController.get().isPlanning() && mRoutingPlanInplaceController != null)
|
||||
{
|
||||
final int height = mRoutingPlanInplaceController.calcHeight();
|
||||
if (height != 0)
|
||||
offsetY = height;
|
||||
}
|
||||
updateCompassOffset(offsetY, offsetX);
|
||||
}
|
||||
TrackRecordingService.stopService(getApplicationContext());
|
||||
mMapButtonsViewModel.setTrackRecorderState(false);
|
||||
}
|
||||
|
@ -2309,15 +2399,15 @@ public class MwmActivity extends BaseMwmFragmentActivity
|
|||
mAlertDialog = new StackedButtonsDialog.Builder(this)
|
||||
.setTitle(R.string.track_recording_alert_title)
|
||||
.setCancelable(false)
|
||||
// Negative/Positive/Neutral doesn't do not have the usual meaning here.
|
||||
.setPositiveButton(R.string.continue_recording, (dialog, which) -> {
|
||||
// Negative/Positive/Neutral do not have their usual meaning here.
|
||||
.setNegativeButton(R.string.continue_recording, (dialog, which) -> {
|
||||
mAlertDialog = null;
|
||||
})
|
||||
.setNeutralButton(R.string.stop_without_saving, (dialog, which) -> {
|
||||
stopTrackRecording();
|
||||
mAlertDialog = null;
|
||||
})
|
||||
.setNegativeButton(R.string.save, (dialog, which) -> {
|
||||
.setPositiveButton(R.string.save, (dialog, which) -> {
|
||||
saveAndStopTrackRecording();
|
||||
mAlertDialog = null;
|
||||
})
|
||||
|
|
|
@ -16,13 +16,14 @@ import androidx.lifecycle.LifecycleObserver;
|
|||
import androidx.lifecycle.LifecycleOwner;
|
||||
import androidx.lifecycle.ProcessLifecycleOwner;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
import app.organicmaps.background.OsmUploadWork;
|
||||
import app.organicmaps.downloader.Android7RootCertificateWorkaround;
|
||||
import app.organicmaps.downloader.DownloaderNotifier;
|
||||
import app.organicmaps.bookmarks.data.BookmarkManager;
|
||||
import app.organicmaps.display.DisplayManager;
|
||||
import app.organicmaps.downloader.CountryItem;
|
||||
import app.organicmaps.downloader.MapManager;
|
||||
import app.organicmaps.downloader.Android7RootCertificateWorkaround;
|
||||
import app.organicmaps.downloader.DownloaderNotifier;
|
||||
import app.organicmaps.location.LocationHelper;
|
||||
import app.organicmaps.location.LocationState;
|
||||
import app.organicmaps.location.SensorHelper;
|
||||
|
@ -33,7 +34,7 @@ import app.organicmaps.maplayer.subway.SubwayManager;
|
|||
import app.organicmaps.maplayer.traffic.TrafficManager;
|
||||
import app.organicmaps.routing.NavigationService;
|
||||
import app.organicmaps.routing.RoutingController;
|
||||
import app.organicmaps.search.SearchEngine;
|
||||
import app.organicmaps.sdk.search.SearchEngine;
|
||||
import app.organicmaps.settings.StoragePathManager;
|
||||
import app.organicmaps.sound.TtsPlayer;
|
||||
import app.organicmaps.util.Config;
|
||||
|
@ -46,10 +47,6 @@ import app.organicmaps.util.Utils;
|
|||
import app.organicmaps.util.log.Logger;
|
||||
import app.organicmaps.util.log.LogsManager;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.List;
|
||||
|
||||
public class MwmApplication extends Application implements Application.ActivityLifecycleCallbacks
|
||||
{
|
||||
@NonNull
|
||||
|
@ -78,9 +75,6 @@ public class MwmApplication extends Application implements Application.ActivityL
|
|||
private volatile boolean mFrameworkInitialized;
|
||||
private volatile boolean mPlatformInitialized;
|
||||
|
||||
@NonNull
|
||||
private final MapManager.StorageCallback mStorageCallbacks = new StorageCallbackImpl();
|
||||
|
||||
@Nullable
|
||||
private WeakReference<Activity> mTopActivity;
|
||||
|
||||
|
@ -127,6 +121,9 @@ public class MwmApplication extends Application implements Application.ActivityL
|
|||
return (MwmApplication) context.getApplicationContext();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static MwmApplication sInstance;
|
||||
|
||||
@NonNull
|
||||
public static SharedPreferences prefs(@NonNull Context context)
|
||||
{
|
||||
|
@ -138,6 +135,9 @@ public class MwmApplication extends Application implements Application.ActivityL
|
|||
{
|
||||
super.onCreate();
|
||||
Logger.i(TAG, "Initializing application");
|
||||
|
||||
sInstance = this;
|
||||
|
||||
LogsManager.INSTANCE.initFileLogging(this);
|
||||
|
||||
Android7RootCertificateWorkaround.initializeIfNeeded(this);
|
||||
|
@ -230,8 +230,6 @@ public class MwmApplication extends Application implements Application.ActivityL
|
|||
|
||||
nativeInitFramework(onComplete);
|
||||
|
||||
MapManager.nativeSubscribe(mStorageCallbacks);
|
||||
|
||||
initNativeStrings();
|
||||
ThemeSwitcher.INSTANCE.initialize(this);
|
||||
SearchEngine.INSTANCE.initialize();
|
||||
|
@ -363,25 +361,4 @@ public class MwmApplication extends Application implements Application.ActivityL
|
|||
mLocationHelper.stop();
|
||||
}
|
||||
}
|
||||
|
||||
private class StorageCallbackImpl implements MapManager.StorageCallback
|
||||
{
|
||||
@Override
|
||||
public void onStatusChanged(List<MapManager.StorageCallbackData> data)
|
||||
{
|
||||
for (MapManager.StorageCallbackData item : data)
|
||||
if (item.isLeafNode && item.newStatus == CountryItem.STATUS_FAILED)
|
||||
{
|
||||
if (MapManager.nativeIsAutoretryFailed())
|
||||
{
|
||||
DownloaderNotifier.notifyDownloadFailed(MwmApplication.this, item.countryId);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProgress(String countryId, long localSize, long remoteSize) {}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -91,7 +91,7 @@ public class SplashActivity extends AppCompatActivity
|
|||
super.onResume();
|
||||
if (mCanceled)
|
||||
return;
|
||||
if (!Config.isLocationRequested() && !LocationUtils.checkCoarseLocationPermission(this))
|
||||
if (!Config.isLocationRequested() && !LocationUtils.checkLocationPermission(this))
|
||||
{
|
||||
Logger.d(TAG, "Requesting location permissions");
|
||||
mPermissionRequest.launch(new String[]{
|
||||
|
|
|
@ -142,7 +142,7 @@ public class BookmarkCategoriesAdapter extends BaseBookmarkCategoryAdapter<Recyc
|
|||
case TYPE_ACTION_ADD ->
|
||||
{
|
||||
Holders.GeneralViewHolder generalViewHolder = (Holders.GeneralViewHolder) holder;
|
||||
generalViewHolder.getImage().setImageResource(R.drawable.ic_import);
|
||||
generalViewHolder.getImage().setImageResource(R.drawable.ic_add_list);
|
||||
generalViewHolder.getText().setText(R.string.bookmarks_create_new_group);
|
||||
}
|
||||
case TYPE_ACTION_IMPORT ->
|
||||
|
|
|
@ -15,6 +15,7 @@ import android.view.View;
|
|||
import android.widget.Toast;
|
||||
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.activity.result.contract.ActivityResultContracts;
|
||||
import androidx.annotation.CallSuper;
|
||||
import androidx.annotation.LayoutRes;
|
||||
import androidx.annotation.NonNull;
|
||||
|
@ -57,9 +58,6 @@ public class BookmarkCategoriesFragment extends BaseMwmRecyclerFragment<Bookmark
|
|||
{
|
||||
private static final String TAG = BookmarkCategoriesFragment.class.getSimpleName();
|
||||
|
||||
static final int REQ_CODE_DELETE_CATEGORY = 102;
|
||||
static final int REQ_CODE_IMPORT_DIRECTORY = 103;
|
||||
|
||||
private static final int MAX_CATEGORY_NAME_LENGTH = 60;
|
||||
|
||||
public static final String BOOKMARKS_CATEGORIES_MENU_ID = "BOOKMARKS_CATEGORIES_BOTTOM_SHEET";
|
||||
|
@ -75,6 +73,22 @@ public class BookmarkCategoriesFragment extends BaseMwmRecyclerFragment<Bookmark
|
|||
@NonNull
|
||||
private DataChangedListener mCategoriesAdapterObserver;
|
||||
|
||||
private final ActivityResultLauncher<Intent> startBookmarkListForResult = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), activityResult -> {
|
||||
if( activityResult.getResultCode() == Activity.RESULT_OK)
|
||||
onDeleteActionSelected(getSelectedCategory());
|
||||
});
|
||||
|
||||
private final ActivityResultLauncher<Intent> startImportDirectoryForResult = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), activityResult ->
|
||||
{
|
||||
if( activityResult.getResultCode() == Activity.RESULT_OK)
|
||||
onImportDirectoryResult(activityResult.getData());
|
||||
});
|
||||
|
||||
private final ActivityResultLauncher<Intent> startBookmarkSettingsForResult = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), activityResult -> {
|
||||
// not handled at the moment
|
||||
});
|
||||
|
||||
|
||||
@Override
|
||||
@LayoutRes
|
||||
protected int getLayoutRes()
|
||||
|
@ -259,7 +273,7 @@ public class BookmarkCategoriesFragment extends BaseMwmRecyclerFragment<Bookmark
|
|||
|
||||
PackageManager packageManager = requireActivity().getPackageManager();
|
||||
if (intent.resolveActivity(packageManager) != null)
|
||||
startActivityForResult(intent, REQ_CODE_IMPORT_DIRECTORY);
|
||||
startImportDirectoryForResult.launch(intent);
|
||||
else
|
||||
showNoFileManagerError();
|
||||
}
|
||||
|
@ -275,7 +289,7 @@ public class BookmarkCategoriesFragment extends BaseMwmRecyclerFragment<Bookmark
|
|||
public void onItemClick(@NonNull View v, @NonNull BookmarkCategory category)
|
||||
{
|
||||
mSelectedCategory = category;
|
||||
BookmarkListActivity.startForResult(this, category);
|
||||
BookmarkListActivity.startForResult(this, startBookmarkListForResult, category);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -303,54 +317,42 @@ public class BookmarkCategoriesFragment extends BaseMwmRecyclerFragment<Bookmark
|
|||
|
||||
private void onSettingsActionSelected(@NonNull BookmarkCategory category)
|
||||
{
|
||||
BookmarkCategorySettingsActivity.startForResult(this, category);
|
||||
BookmarkCategorySettingsActivity.startForResult(this, startBookmarkSettingsForResult, category);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void onActivityResult(int requestCode, int resultCode, Intent data)
|
||||
private void onImportDirectoryResult(Intent data)
|
||||
{
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
if (resultCode != Activity.RESULT_OK)
|
||||
return;
|
||||
switch (requestCode)
|
||||
{
|
||||
case REQ_CODE_DELETE_CATEGORY -> onDeleteActionSelected(getSelectedCategory());
|
||||
case REQ_CODE_IMPORT_DIRECTORY ->
|
||||
{
|
||||
if (data == null)
|
||||
throw new AssertionError("Data is null");
|
||||
if (data == null)
|
||||
throw new AssertionError("Data is null");
|
||||
|
||||
final Context context = requireActivity();
|
||||
final Uri rootUri = data.getData();
|
||||
final ProgressDialog dialog = new ProgressDialog(context, R.style.MwmTheme_ProgressDialog);
|
||||
dialog.setMessage(getString(R.string.wait_several_minutes));
|
||||
dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
|
||||
dialog.setIndeterminate(true);
|
||||
dialog.setCancelable(false);
|
||||
dialog.show();
|
||||
Logger.d(TAG, "Importing bookmarks from " + rootUri);
|
||||
MwmApplication app = MwmApplication.from(context);
|
||||
final File tempDir = new File(StorageUtils.getTempPath(app));
|
||||
final ContentResolver resolver = context.getContentResolver();
|
||||
ThreadPool.getStorage().execute(() -> {
|
||||
AtomicInteger found = new AtomicInteger(0);
|
||||
StorageUtils.listContentProviderFilesRecursively(
|
||||
final Context context = requireActivity();
|
||||
final Uri rootUri = data.getData();
|
||||
final ProgressDialog dialog = new ProgressDialog(context, R.style.MwmTheme_ProgressDialog);
|
||||
dialog.setMessage(getString(R.string.wait_several_minutes));
|
||||
dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
|
||||
dialog.setIndeterminate(true);
|
||||
dialog.setCancelable(false);
|
||||
dialog.show();
|
||||
Logger.d(TAG, "Importing bookmarks from " + rootUri);
|
||||
MwmApplication app = MwmApplication.from(context);
|
||||
final File tempDir = new File(StorageUtils.getTempPath(app));
|
||||
final ContentResolver resolver = context.getContentResolver();
|
||||
ThreadPool.getStorage().execute(() -> {
|
||||
AtomicInteger found = new AtomicInteger(0);
|
||||
StorageUtils.listContentProviderFilesRecursively(
|
||||
resolver, rootUri, uri -> {
|
||||
if (BookmarkManager.INSTANCE.importBookmarksFile(resolver, uri, tempDir))
|
||||
found.incrementAndGet();
|
||||
});
|
||||
UiThread.run(() -> {
|
||||
if (dialog.isShowing())
|
||||
dialog.dismiss();
|
||||
int found_val = found.get();
|
||||
String message = context.getResources().getQuantityString(
|
||||
UiThread.run(() -> {
|
||||
if (dialog.isShowing())
|
||||
dialog.dismiss();
|
||||
int found_val = found.get();
|
||||
String message = context.getResources().getQuantityString(
|
||||
R.plurals.bookmarks_detect_message, found_val, found_val);
|
||||
Toast.makeText(requireContext(), message, Toast.LENGTH_LONG).show();
|
||||
});
|
||||
});
|
||||
}
|
||||
default -> throw new AssertionError("Invalid requestCode: " + requestCode);
|
||||
}
|
||||
Toast.makeText(requireContext(), message, Toast.LENGTH_LONG).show();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -2,6 +2,7 @@ package app.organicmaps.bookmarks;
|
|||
|
||||
import android.content.Intent;
|
||||
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
|
@ -11,7 +12,6 @@ import app.organicmaps.bookmarks.data.BookmarkCategory;
|
|||
|
||||
public class BookmarkCategorySettingsActivity extends BaseMwmFragmentActivity
|
||||
{
|
||||
public static final int REQUEST_CODE = 107;
|
||||
public static final String EXTRA_BOOKMARK_CATEGORY = "bookmark_category";
|
||||
|
||||
@Override
|
||||
|
@ -32,11 +32,11 @@ public class BookmarkCategorySettingsActivity extends BaseMwmFragmentActivity
|
|||
return BookmarkCategorySettingsFragment.class;
|
||||
}
|
||||
|
||||
public static void startForResult(@NonNull Fragment fragment,
|
||||
public static void startForResult(@NonNull Fragment fragment, ActivityResultLauncher<Intent> startBookmarkSettingsForResult,
|
||||
@NonNull BookmarkCategory category)
|
||||
{
|
||||
android.content.Intent intent = new Intent(fragment.requireActivity(), BookmarkCategorySettingsActivity.class)
|
||||
.putExtra(EXTRA_BOOKMARK_CATEGORY, category);
|
||||
fragment.startActivityForResult(intent, REQUEST_CODE);
|
||||
startBookmarkSettingsForResult.launch(intent);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package app.organicmaps.bookmarks;
|
|||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.annotation.CallSuper;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.StyleRes;
|
||||
|
@ -57,11 +58,11 @@ public class BookmarkListActivity extends BaseToolbarActivity
|
|||
return R.layout.bookmarks_activity;
|
||||
}
|
||||
|
||||
static void startForResult(@NonNull Fragment fragment, @NonNull BookmarkCategory category)
|
||||
static void startForResult(@NonNull Fragment fragment, ActivityResultLauncher<Intent> startBookmarkListForResult, @NonNull BookmarkCategory category)
|
||||
{
|
||||
Bundle args = new Bundle();
|
||||
Intent intent = new Intent(fragment.requireActivity(), BookmarkListActivity.class);
|
||||
intent.putExtra(BookmarksListFragment.EXTRA_CATEGORY, category);
|
||||
fragment.startActivityForResult(intent, BookmarkCategoriesFragment.REQ_CODE_DELETE_CATEGORY);
|
||||
startBookmarkListForResult.launch(intent);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ import android.view.ViewGroup;
|
|||
import android.widget.ImageView;
|
||||
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.activity.result.contract.ActivityResultContracts;
|
||||
import androidx.annotation.CallSuper;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
@ -38,8 +39,8 @@ import app.organicmaps.bookmarks.data.KmlFileType;
|
|||
import app.organicmaps.bookmarks.data.SortedBlock;
|
||||
import app.organicmaps.bookmarks.data.Track;
|
||||
import app.organicmaps.location.LocationHelper;
|
||||
import app.organicmaps.search.NativeBookmarkSearchListener;
|
||||
import app.organicmaps.search.SearchEngine;
|
||||
import app.organicmaps.sdk.search.BookmarkSearchListener;
|
||||
import app.organicmaps.sdk.search.SearchEngine;
|
||||
import app.organicmaps.util.Graphics;
|
||||
import app.organicmaps.util.SharingUtils;
|
||||
import app.organicmaps.util.UiUtils;
|
||||
|
@ -61,7 +62,7 @@ public class BookmarksListFragment extends BaseMwmRecyclerFragment<ConcatAdapter
|
|||
implements BookmarkManager.BookmarksSharingListener,
|
||||
BookmarkManager.BookmarksSortingListener,
|
||||
BookmarkManager.BookmarksLoadingListener,
|
||||
NativeBookmarkSearchListener,
|
||||
BookmarkSearchListener,
|
||||
ChooseBookmarksSortingTypeFragment.ChooseSortingTypeListener,
|
||||
MenuBottomSheetFragment.MenuBottomSheetInterface
|
||||
{
|
||||
|
@ -74,6 +75,15 @@ public class BookmarksListFragment extends BaseMwmRecyclerFragment<ConcatAdapter
|
|||
private static final String OPTIONS_MENU_ID = "OPTIONS_MENU_BOTTOM_SHEET";
|
||||
|
||||
private ActivityResultLauncher<SharingUtils.SharingIntent> shareLauncher;
|
||||
private final ActivityResultLauncher<Intent> startBookmarkListForResult = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), activityResult -> {
|
||||
System.out.println("resultCode: " + activityResult.getResultCode());
|
||||
handleActivityResult();
|
||||
});
|
||||
|
||||
private final ActivityResultLauncher<Intent> startBookmarkSettingsForResult = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), activityResult -> {
|
||||
System.out.println("resultCode: " + activityResult.getResultCode());
|
||||
handleActivityResult();
|
||||
});
|
||||
|
||||
@SuppressWarnings("NotNullFieldNotInitialized")
|
||||
@NonNull
|
||||
|
@ -140,7 +150,9 @@ public class BookmarksListFragment extends BaseMwmRecyclerFragment<ConcatAdapter
|
|||
|
||||
BookmarkCollectionAdapter adapter = new BookmarkCollectionAdapter(getCategoryOrThrow(),
|
||||
mCategoryItems);
|
||||
adapter.setOnClickListener((v, item) -> BookmarkListActivity.startForResult(this, item));
|
||||
adapter.setOnClickListener((v, item) -> {
|
||||
BookmarkListActivity.startForResult(this, startBookmarkListForResult, item);
|
||||
});
|
||||
|
||||
return adapter;
|
||||
}
|
||||
|
@ -756,7 +768,7 @@ public class BookmarksListFragment extends BaseMwmRecyclerFragment<ConcatAdapter
|
|||
|
||||
private void onSettingsOptionSelected()
|
||||
{
|
||||
BookmarkCategorySettingsActivity.startForResult(this, mCategoryDataSource.getData());
|
||||
BookmarkCategorySettingsActivity.startForResult(this, startBookmarkSettingsForResult, mCategoryDataSource.getData());
|
||||
}
|
||||
|
||||
private void onDeleteOptionSelected()
|
||||
|
@ -795,21 +807,25 @@ public class BookmarksListFragment extends BaseMwmRecyclerFragment<ConcatAdapter
|
|||
{
|
||||
ArrayList<MenuBottomSheetItem> items = new ArrayList<>();
|
||||
items.add(new MenuBottomSheetItem(R.string.edit, R.drawable.ic_edit, this::onTrackEditActionSelected));
|
||||
items.add(new MenuBottomSheetItem(R.string.export_file, R.drawable.ic_file_kmz, () -> onShareTrackSelected(track.getTrackId(), KmlFileType.Text)));
|
||||
items.add(new MenuBottomSheetItem(R.string.export_file_gpx, R.drawable.ic_file_gpx, () -> onShareTrackSelected(track.getTrackId(), KmlFileType.Gpx)));
|
||||
items.add(new MenuBottomSheetItem(R.string.delete, R.drawable.ic_delete, () -> onDeleteTrackSelected(track.getTrackId())));
|
||||
return items;
|
||||
}
|
||||
|
||||
private void onShareTrackSelected(long trackId, KmlFileType kmlFileType)
|
||||
{
|
||||
BookmarksSharingHelper.INSTANCE.prepareTrackForSharing(requireActivity(), trackId, kmlFileType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPreparedFileForSharing(@NonNull BookmarkSharingResult result)
|
||||
{
|
||||
BookmarksSharingHelper.INSTANCE.onPreparedFileForSharing(requireActivity(), shareLauncher, result);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation") // https://github.com/organicmaps/organicmaps/issues/3630
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data)
|
||||
private void handleActivityResult()
|
||||
{
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
getBookmarkListAdapter().notifyDataSetChanged();
|
||||
ActionBar actionBar = ((AppCompatActivity) requireActivity()).getSupportActionBar();
|
||||
actionBar.setTitle(mCategoryDataSource.getData().getName());
|
||||
|
|
|
@ -36,6 +36,12 @@ public enum BookmarksSharingHelper
|
|||
BookmarkManager.INSTANCE.prepareCategoriesForSharing(new long[]{catId}, kmlFileType);
|
||||
}
|
||||
|
||||
public void prepareTrackForSharing(@NonNull Activity context, long trackId, KmlFileType kmlFileType)
|
||||
{
|
||||
showProgressDialog(context);
|
||||
BookmarkManager.INSTANCE.prepareTrackForSharing(trackId, kmlFileType);
|
||||
}
|
||||
|
||||
private void showProgressDialog(@NonNull Activity context)
|
||||
{
|
||||
mProgressDialog = new ProgressDialog(context, R.style.MwmTheme_ProgressDialog);
|
||||
|
|
|
@ -11,7 +11,7 @@ import androidx.core.os.ParcelCompat;
|
|||
|
||||
import app.organicmaps.Framework;
|
||||
import app.organicmaps.routing.RoutePointInfo;
|
||||
import app.organicmaps.search.Popularity;
|
||||
import app.organicmaps.sdk.search.Popularity;
|
||||
import app.organicmaps.util.Constants;
|
||||
|
||||
// TODO consider refactoring to remove hack with MapObject unmarshalling itself and Bookmark at the same time.
|
||||
|
|
|
@ -527,6 +527,11 @@ public enum BookmarkManager
|
|||
nativePrepareFileForSharing(catIds, kmlFileType.ordinal());
|
||||
}
|
||||
|
||||
public void prepareTrackForSharing(long trackId, KmlFileType kmlFileType)
|
||||
{
|
||||
nativePrepareTrackFileForSharing(trackId, kmlFileType.ordinal());
|
||||
}
|
||||
|
||||
public void setNotificationsEnabled(boolean enabled)
|
||||
{
|
||||
nativeSetNotificationsEnabled(enabled);
|
||||
|
@ -806,6 +811,8 @@ public enum BookmarkManager
|
|||
|
||||
private static native void nativePrepareFileForSharing(long[] catIds, int kmlFileType);
|
||||
|
||||
private static native void nativePrepareTrackFileForSharing(long trackId, int kmlFileType);
|
||||
|
||||
private static native boolean nativeIsCategoryEmpty(long catId);
|
||||
|
||||
private static native void nativeSetNotificationsEnabled(boolean enabled);
|
||||
|
|
|
@ -12,7 +12,7 @@ import androidx.core.os.ParcelCompat;
|
|||
|
||||
import app.organicmaps.Framework;
|
||||
import app.organicmaps.routing.RoutePointInfo;
|
||||
import app.organicmaps.search.Popularity;
|
||||
import app.organicmaps.sdk.search.Popularity;
|
||||
import app.organicmaps.util.Utils;
|
||||
import app.organicmaps.widget.placepage.PlacePageData;
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package app.organicmaps.car;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
|
@ -23,7 +22,6 @@ import androidx.lifecycle.LifecycleOwner;
|
|||
import app.organicmaps.BuildConfig;
|
||||
import app.organicmaps.R;
|
||||
import app.organicmaps.api.Const;
|
||||
import app.organicmaps.routing.NavigationService;
|
||||
|
||||
public final class CarAppService extends androidx.car.app.CarAppService
|
||||
{
|
||||
|
@ -53,20 +51,7 @@ public final class CarAppService extends androidx.car.app.CarAppService
|
|||
public Session onCreateSession(@Nullable SessionInfo sessionInfo)
|
||||
{
|
||||
createNotificationChannel();
|
||||
startForeground(NOTIFICATION_ID, getNotification());
|
||||
final CarAppSession carAppSession = new CarAppSession(sessionInfo);
|
||||
carAppSession.getLifecycle().addObserver(new DefaultLifecycleObserver()
|
||||
{
|
||||
@Override
|
||||
public void onDestroy(@NonNull LifecycleOwner owner)
|
||||
{
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
|
||||
stopForeground(STOP_FOREGROUND_REMOVE);
|
||||
else
|
||||
stopForeground(true);
|
||||
}
|
||||
});
|
||||
return carAppSession;
|
||||
return new CarAppSession(sessionInfo);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
|
@ -104,13 +89,4 @@ public final class CarAppService extends androidx.car.app.CarAppService
|
|||
.build();
|
||||
notificationManager.createNotificationChannel(notificationChannel);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private Notification getNotification()
|
||||
{
|
||||
return NavigationService.getNotificationBuilder(this)
|
||||
.setChannelId(ANDROID_AUTO_NOTIFICATION_CHANNEL_ID)
|
||||
.setContentTitle(getString(R.string.aa_connected_to_car_notification_title))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -109,7 +109,7 @@ class DownloaderScreen extends BaseScreen
|
|||
for (final var item : mMissingMaps.entrySet())
|
||||
{
|
||||
item.getValue().update();
|
||||
MapManager.nativeDownload(item.getKey());
|
||||
MapManager.startDownload(item.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -150,12 +150,11 @@ class DownloaderScreen extends BaseScreen
|
|||
return getCarContext().getString(R.string.downloader_loading_ios);
|
||||
|
||||
final long downloadedSize = getDownloadedSize();
|
||||
final float progress = (float) downloadedSize / mTotalSize * 100;
|
||||
final String progressPercent = StringUtils.formatPercent((double) downloadedSize / mTotalSize);
|
||||
final String totalSizeStr = StringUtils.getFileSizeString(getCarContext(), mTotalSize);
|
||||
final String downloadedSizeStr = StringUtils.getFileSizeString(getCarContext(), downloadedSize);
|
||||
|
||||
return StringUtils.formatUsingSystemLocale("%.2f%%\n%s",
|
||||
progress, downloadedSizeStr + " / " + totalSizeStr);
|
||||
return progressPercent + "\n" + downloadedSizeStr + " / " + totalSizeStr;
|
||||
}
|
||||
|
||||
private long getDownloadedSize()
|
||||
|
|
|
@ -113,7 +113,7 @@ public class RequestPermissionsScreenWithNotification extends BaseScreen impleme
|
|||
.setOngoing(true)
|
||||
.setShowWhen(false)
|
||||
.setOnlyAlertOnce(true)
|
||||
.setSmallIcon(R.drawable.ic_my_location)
|
||||
.setSmallIcon(R.drawable.ic_location_crosshair)
|
||||
.setColor(ContextCompat.getColor(getCarContext(), R.color.notification))
|
||||
.setContentTitle(getCarContext().getString(R.string.aa_request_permission_notification))
|
||||
.setContentIntent(pendingIntent);
|
||||
|
|
|
@ -23,13 +23,13 @@ import app.organicmaps.car.SurfaceRenderer;
|
|||
import app.organicmaps.car.screens.base.BaseMapScreen;
|
||||
import app.organicmaps.car.util.UiHelpers;
|
||||
import app.organicmaps.location.LocationHelper;
|
||||
import app.organicmaps.search.NativeSearchListener;
|
||||
import app.organicmaps.search.SearchEngine;
|
||||
import app.organicmaps.search.SearchRecents;
|
||||
import app.organicmaps.search.SearchResult;
|
||||
import app.organicmaps.sdk.search.SearchListener;
|
||||
import app.organicmaps.sdk.search.SearchEngine;
|
||||
import app.organicmaps.sdk.search.SearchRecents;
|
||||
import app.organicmaps.sdk.search.SearchResult;
|
||||
import app.organicmaps.util.Language;
|
||||
|
||||
public class SearchOnMapScreen extends BaseMapScreen implements NativeSearchListener
|
||||
public class SearchOnMapScreen extends BaseMapScreen implements SearchListener
|
||||
{
|
||||
private final int MAX_RESULTS_SIZE;
|
||||
|
||||
|
|
|
@ -21,13 +21,13 @@ import app.organicmaps.bookmarks.data.MapObject;
|
|||
import app.organicmaps.car.SurfaceRenderer;
|
||||
import app.organicmaps.car.screens.base.BaseMapScreen;
|
||||
import app.organicmaps.location.LocationHelper;
|
||||
import app.organicmaps.search.NativeSearchListener;
|
||||
import app.organicmaps.search.SearchEngine;
|
||||
import app.organicmaps.search.SearchRecents;
|
||||
import app.organicmaps.search.SearchResult;
|
||||
import app.organicmaps.sdk.search.SearchListener;
|
||||
import app.organicmaps.sdk.search.SearchEngine;
|
||||
import app.organicmaps.sdk.search.SearchRecents;
|
||||
import app.organicmaps.sdk.search.SearchResult;
|
||||
import app.organicmaps.util.Language;
|
||||
|
||||
public class SearchScreen extends BaseMapScreen implements SearchTemplate.SearchCallback, NativeSearchListener
|
||||
public class SearchScreen extends BaseMapScreen implements SearchTemplate.SearchCallback, SearchListener
|
||||
{
|
||||
private final int MAX_RESULTS_SIZE;
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ import androidx.car.app.model.ForegroundCarColorSpan;
|
|||
import app.organicmaps.R;
|
||||
import app.organicmaps.car.util.Colors;
|
||||
import app.organicmaps.car.util.RoutingHelpers;
|
||||
import app.organicmaps.search.SearchResult;
|
||||
import app.organicmaps.sdk.search.SearchResult;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
|
|
@ -8,7 +8,7 @@ import androidx.car.app.suggestion.model.Suggestion;
|
|||
import androidx.core.graphics.drawable.IconCompat;
|
||||
|
||||
import app.organicmaps.R;
|
||||
import app.organicmaps.search.SearchRecents;
|
||||
import app.organicmaps.sdk.search.SearchRecents;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
|
|
@ -1,69 +0,0 @@
|
|||
package app.organicmaps.dialog;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import app.organicmaps.R;
|
||||
|
||||
public class ProgressDialogFragment extends DialogFragment
|
||||
{
|
||||
private static final String ARG_MESSAGE = "title";
|
||||
private static final String ARG_CANCELABLE = "cancelable";
|
||||
private static final String ARG_RETAIN_INSTANCE = "retain_instance";
|
||||
|
||||
public ProgressDialogFragment()
|
||||
{
|
||||
// Do nothing by default.
|
||||
}
|
||||
|
||||
protected void setCancelResult()
|
||||
{
|
||||
Fragment targetFragment = getTargetFragment();
|
||||
if (targetFragment != null)
|
||||
targetFragment.onActivityResult(getTargetRequestCode(), Activity.RESULT_CANCELED, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState)
|
||||
{
|
||||
super.onCreate(savedInstanceState);
|
||||
Bundle args = requireArguments();
|
||||
setRetainInstance(args.getBoolean(ARG_RETAIN_INSTANCE, true));
|
||||
setCancelable(args.getBoolean(ARG_CANCELABLE, false));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable
|
||||
Bundle savedInstanceState)
|
||||
{
|
||||
View view = inflater.inflate(R.layout.indeterminated_progress_dialog, container, false);
|
||||
Bundle args = requireArguments();
|
||||
TextView messageView = view.findViewById(R.id.message);
|
||||
messageView.setText(args.getString(ARG_MESSAGE));
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancel(DialogInterface dialog)
|
||||
{
|
||||
setCancelResult();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView()
|
||||
{
|
||||
if (getDialog() != null && getRetainInstance())
|
||||
getDialog().setDismissMessage(null);
|
||||
super.onDestroyView();
|
||||
}
|
||||
}
|
|
@ -30,7 +30,7 @@ class BottomPanel
|
|||
public void onClick(View v)
|
||||
{
|
||||
final String country = mFragment.getCurrentRoot();
|
||||
MapManager.warnOn3gUpdate(mFragment.requireActivity(), country, () -> MapManager.nativeUpdate(country));
|
||||
MapManager.warnOn3gUpdate(mFragment.requireActivity(), country, () -> MapManager.startUpdate(country));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -63,7 +63,9 @@ public final class CountryItem implements Comparable<CountryItem>
|
|||
public int errorCode;
|
||||
public boolean present;
|
||||
|
||||
// Progress
|
||||
/**
|
||||
* This value represents the percentage of download (values span from 0 to 100)
|
||||
*/
|
||||
public float progress;
|
||||
public long downloadedBytes;
|
||||
public long bytesToDownload;
|
||||
|
|
|
@ -192,8 +192,7 @@ public class CountrySuggestFragment extends BaseMwmFragment implements View.OnCl
|
|||
|
||||
private void updateProgress()
|
||||
{
|
||||
String text = StringUtils.formatUsingSystemLocale("%1$s %2$.2f%%", getString(R.string.downloader_downloading),
|
||||
mDownloadingCountry.progress);
|
||||
String text = getString(R.string.downloader_downloading) + " " + StringUtils.formatPercent(mDownloadingCountry.progress / 100);
|
||||
mTvProgress.setText(text);
|
||||
mWpvDownloadProgress.setProgress(Math.round(mDownloadingCountry.progress));
|
||||
}
|
||||
|
|
|
@ -91,7 +91,7 @@ class DownloaderAdapter extends RecyclerView.Adapter<DownloaderAdapter.ViewHolde
|
|||
item.update();
|
||||
if (item.status != CountryItem.STATUS_UPDATABLE)
|
||||
return;
|
||||
MapManager.warnOn3gUpdate(adapter.mActivity, item.id, () -> MapManager.nativeUpdate(item.id));
|
||||
MapManager.warnOn3gUpdate(adapter.mActivity, item.id, () -> MapManager.startUpdate(item.id));
|
||||
}
|
||||
|
||||
private void onExploreActionSelected(CountryItem item, DownloaderAdapter adapter)
|
||||
|
@ -150,6 +150,7 @@ class DownloaderAdapter extends RecyclerView.Adapter<DownloaderAdapter.ViewHolde
|
|||
((MwmActivity) adapter.mActivity).closePlacePage();
|
||||
}
|
||||
deleteNode(item);
|
||||
refreshData();
|
||||
}
|
||||
|
||||
private record PathEntry(CountryItem item, boolean myMapsMode, int topPosition, int topOffset)
|
||||
|
@ -204,14 +205,9 @@ class DownloaderAdapter extends RecyclerView.Adapter<DownloaderAdapter.ViewHolde
|
|||
}
|
||||
}
|
||||
|
||||
if (mSearchResultsMode)
|
||||
for (MapManager.StorageCallbackData item : data)
|
||||
{
|
||||
for (MapManager.StorageCallbackData item : data)
|
||||
updateItem(item.countryId);
|
||||
}
|
||||
else
|
||||
{
|
||||
refreshData();
|
||||
updateItem(item.countryId);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -386,12 +382,10 @@ class DownloaderAdapter extends RecyclerView.Adapter<DownloaderAdapter.ViewHolde
|
|||
}
|
||||
case CountryItem.STATUS_FAILED ->
|
||||
{
|
||||
RetryFailedDownloadConfirmationListener listener =
|
||||
new RetryFailedDownloadConfirmationListener(mActivity.getApplication());
|
||||
MapManager.warn3gAndRetry(mActivity, mItem.id, listener);
|
||||
MapManager.warn3gAndRetry(mActivity, mItem.id, null);
|
||||
}
|
||||
case CountryItem.STATUS_UPDATABLE ->
|
||||
MapManager.warnOn3gUpdate(mActivity, mItem.id, () -> MapManager.nativeUpdate(mItem.id));
|
||||
MapManager.warnOn3gUpdate(mActivity, mItem.id, () -> MapManager.startUpdate(mItem.id));
|
||||
default -> throw new IllegalArgumentException("Inappropriate item status: " + mItem.status);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,8 @@ import android.os.Bundle;
|
|||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.activity.result.contract.ActivityResultContracts;
|
||||
import androidx.annotation.CallSuper;
|
||||
import androidx.annotation.Keep;
|
||||
import androidx.annotation.NonNull;
|
||||
|
@ -15,8 +17,8 @@ import androidx.recyclerview.widget.RecyclerView;
|
|||
import app.organicmaps.R;
|
||||
import app.organicmaps.base.BaseMwmRecyclerFragment;
|
||||
import app.organicmaps.base.OnBackPressListener;
|
||||
import app.organicmaps.search.NativeMapSearchListener;
|
||||
import app.organicmaps.search.SearchEngine;
|
||||
import app.organicmaps.sdk.search.MapSearchListener;
|
||||
import app.organicmaps.sdk.search.SearchEngine;
|
||||
import app.organicmaps.widget.PlaceholderView;
|
||||
import app.organicmaps.util.bottomsheet.MenuBottomSheetFragment;
|
||||
import app.organicmaps.util.bottomsheet.MenuBottomSheetItem;
|
||||
|
@ -39,6 +41,8 @@ public class DownloaderFragment extends BaseMwmRecyclerFragment<DownloaderAdapte
|
|||
|
||||
private int mSubscriberSlot;
|
||||
|
||||
final ActivityResultLauncher<Intent> startVoiceRecognitionForResult = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), activityResult -> mToolbarController.onVoiceRecognitionResult(activityResult));
|
||||
|
||||
private final RecyclerView.OnScrollListener mScrollListener = new RecyclerView.OnScrollListener() {
|
||||
@Override
|
||||
public void onScrollStateChanged(RecyclerView recyclerView, int newState)
|
||||
|
@ -48,13 +52,13 @@ public class DownloaderFragment extends BaseMwmRecyclerFragment<DownloaderAdapte
|
|||
}
|
||||
};
|
||||
|
||||
private final NativeMapSearchListener mSearchListener = new NativeMapSearchListener()
|
||||
private final MapSearchListener mSearchListener = new MapSearchListener()
|
||||
{
|
||||
// Called from JNI.
|
||||
@Keep
|
||||
@SuppressWarnings("unused")
|
||||
@Override
|
||||
public void onMapSearchResults(Result[] results, long timestamp, boolean isLast)
|
||||
public void onMapSearchResults(@NonNull Result[] results, long timestamp, boolean isLast)
|
||||
{
|
||||
if (!mSearchRunning || timestamp != mCurrentSearch)
|
||||
return;
|
||||
|
@ -62,8 +66,8 @@ public class DownloaderFragment extends BaseMwmRecyclerFragment<DownloaderAdapte
|
|||
List<CountryItem> rs = new ArrayList<>();
|
||||
for (Result result : results)
|
||||
{
|
||||
CountryItem item = CountryItem.fill(result.countryId);
|
||||
item.searchResultName = result.matchedString;
|
||||
CountryItem item = CountryItem.fill(result.countryId());
|
||||
item.searchResultName = result.matchedString();
|
||||
rs.add(item);
|
||||
}
|
||||
|
||||
|
@ -210,13 +214,6 @@ public class DownloaderFragment extends BaseMwmRecyclerFragment<DownloaderAdapte
|
|||
return mAdapter;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation") // https://github.com/organicmaps/organicmaps/issues/3630
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data)
|
||||
{
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
mToolbarController.onActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
|
|
|
@ -22,18 +22,26 @@ import app.organicmaps.R;
|
|||
import app.organicmaps.util.StringUtils;
|
||||
import app.organicmaps.util.log.Logger;
|
||||
|
||||
public abstract class DownloaderNotifier
|
||||
public class DownloaderNotifier
|
||||
{
|
||||
private static final String TAG = DownloaderNotifier.class.getSimpleName();
|
||||
|
||||
private static final String CHANNEL_ID = "downloader";
|
||||
private static final int NOTIFICATION_ID = 1;
|
||||
public static final int NOTIFICATION_ID = 1;
|
||||
|
||||
private final Context mContext;
|
||||
private NotificationCompat.Builder mProgressNotificationBuilder;
|
||||
|
||||
public DownloaderNotifier(Context context)
|
||||
{
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
public static void createNotificationChannel(@NonNull Context context)
|
||||
{
|
||||
final NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
|
||||
final NotificationChannelCompat channel = new NotificationChannelCompat.Builder(CHANNEL_ID,
|
||||
NotificationManagerCompat.IMPORTANCE_DEFAULT)
|
||||
NotificationManagerCompat.IMPORTANCE_LOW)
|
||||
.setName(context.getString(R.string.notification_channel_downloader))
|
||||
.setShowBadge(true)
|
||||
.setVibrationEnabled(false)
|
||||
|
@ -42,49 +50,93 @@ public abstract class DownloaderNotifier
|
|||
notificationManager.createNotificationChannel(channel);
|
||||
}
|
||||
|
||||
public static void notifyDownloadFailed(@NonNull Context context, @Nullable String countryId)
|
||||
public void notifyDownloadFailed(@Nullable String countryId)
|
||||
{
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU &&
|
||||
ContextCompat.checkSelfPermission(context, POST_NOTIFICATIONS) != PERMISSION_GRANTED)
|
||||
ContextCompat.checkSelfPermission(mContext, POST_NOTIFICATIONS) != PERMISSION_GRANTED)
|
||||
{
|
||||
Logger.w(TAG, "Permission POST_NOTIFICATIONS is not granted, skipping notification");
|
||||
return;
|
||||
}
|
||||
|
||||
final String title = context.getString(R.string.app_name);
|
||||
final String title = mContext.getString(R.string.app_name);
|
||||
final String countryName = MapManager.nativeGetName(countryId);
|
||||
final String content = context.getString(R.string.download_country_failed, countryName);
|
||||
final String content = mContext.getString(R.string.download_country_failed, countryName);
|
||||
|
||||
final int FLAG_IMMUTABLE = Build.VERSION.SDK_INT < Build.VERSION_CODES.M ? 0 : PendingIntent.FLAG_IMMUTABLE;
|
||||
final Intent contentIntent = MwmActivity.createShowMapIntent(context, countryId);
|
||||
contentIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
final PendingIntent contentPendingIntent = PendingIntent.getActivity(context, 0, contentIntent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE);
|
||||
var contentPendingIntent = getNotificationPendingIntent(countryId);
|
||||
|
||||
final Notification notification = new NotificationCompat.Builder(context, CHANNEL_ID)
|
||||
final Notification notification = new NotificationCompat.Builder(mContext, CHANNEL_ID)
|
||||
.setAutoCancel(true)
|
||||
.setCategory(NotificationCompat.CATEGORY_ERROR)
|
||||
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||
.setSmallIcon(R.drawable.ic_splash)
|
||||
.setColor(ContextCompat.getColor(context, R.color.notification))
|
||||
.setColor(ContextCompat.getColor(mContext, R.color.notification))
|
||||
.setContentTitle(title)
|
||||
.setContentText(content)
|
||||
.setShowWhen(true)
|
||||
.setTicker(getTicker(context, title, content))
|
||||
.setTicker(getTicker(mContext, title, content))
|
||||
.setContentIntent(contentPendingIntent)
|
||||
.setOnlyAlertOnce(true)
|
||||
.build();
|
||||
|
||||
Logger.i(TAG, "Notifying about failed map download");
|
||||
final NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
|
||||
final NotificationManagerCompat notificationManager = NotificationManagerCompat.from(mContext);
|
||||
notificationManager.notify(NOTIFICATION_ID, notification);
|
||||
}
|
||||
|
||||
static void cancelNotification(@NonNull Context context)
|
||||
public void notifyProgress() {
|
||||
notifyProgress(null, 0, 0);
|
||||
}
|
||||
|
||||
public void notifyProgress(@Nullable String countryId, int maxProgress, int progress) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU &&
|
||||
ContextCompat.checkSelfPermission(mContext, POST_NOTIFICATIONS) != PERMISSION_GRANTED)
|
||||
{
|
||||
Logger.w(TAG, "Permission POST_NOTIFICATIONS is not granted, skipping notification");
|
||||
return;
|
||||
}
|
||||
|
||||
NotificationManagerCompat.from(mContext).notify(NOTIFICATION_ID, buildProgressNotification(countryId, maxProgress, progress));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Notification buildProgressNotification()
|
||||
{
|
||||
Logger.i(TAG, "Cancelling notification about failed map download");
|
||||
final NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
|
||||
notificationManager.cancel(NOTIFICATION_ID);
|
||||
return buildProgressNotification(null, 0, 0);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Notification buildProgressNotification(@Nullable String countryId, int maxProgress, int progress)
|
||||
{
|
||||
var builder = startNotification(countryId);
|
||||
|
||||
builder.setProgress(maxProgress, progress, maxProgress == 0);
|
||||
builder.setContentText("Download in progress");
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private NotificationCompat.Builder startNotification(@Nullable String countryId)
|
||||
{
|
||||
final String title = mContext.getString(R.string.app_name);
|
||||
|
||||
return new NotificationCompat.Builder(mContext, CHANNEL_ID)
|
||||
.setAutoCancel(true)
|
||||
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||
.setSmallIcon(R.drawable.ic_splash)
|
||||
.setColor(ContextCompat.getColor(mContext, R.color.notification))
|
||||
.setShowWhen(true)
|
||||
.setContentTitle(title)
|
||||
.setContentIntent(getNotificationPendingIntent(countryId));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private PendingIntent getNotificationPendingIntent(@Nullable String countryId) {
|
||||
final int FLAG_IMMUTABLE = Build.VERSION.SDK_INT < Build.VERSION_CODES.M ? 0 : PendingIntent.FLAG_IMMUTABLE;
|
||||
final Intent contentIntent = MwmActivity.createShowMapIntent(mContext, countryId);
|
||||
contentIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
return PendingIntent.getActivity(mContext, 0, contentIntent, PendingIntent.FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
|
|
|
@ -0,0 +1,142 @@
|
|||
package app.organicmaps.downloader;
|
||||
|
||||
import static android.Manifest.permission.POST_NOTIFICATIONS;
|
||||
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
|
||||
|
||||
import android.app.ForegroundServiceStartNotAllowedException;
|
||||
import android.app.Service;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.os.IBinder;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import app.organicmaps.MwmApplication;
|
||||
import app.organicmaps.util.log.Logger;
|
||||
|
||||
public class DownloaderService extends Service implements MapManager.StorageCallback
|
||||
{
|
||||
private static final String TAG = DownloaderService.class.getSimpleName();
|
||||
|
||||
private final DownloaderNotifier mNotifier = new DownloaderNotifier(this);
|
||||
private int mSubscriptionSlot;
|
||||
|
||||
@Override
|
||||
public void onCreate()
|
||||
{
|
||||
super.onCreate();
|
||||
|
||||
Logger.i(TAG);
|
||||
|
||||
mSubscriptionSlot = MapManager.nativeSubscribe(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId)
|
||||
{
|
||||
Logger.i(TAG, "Downloading: " + MapManager.nativeIsDownloading());
|
||||
|
||||
var notification = mNotifier.buildProgressNotification();
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
|
||||
{
|
||||
try
|
||||
{
|
||||
startForeground(DownloaderNotifier.NOTIFICATION_ID, notification);
|
||||
} catch (ForegroundServiceStartNotAllowedException e)
|
||||
{
|
||||
Logger.e(TAG, "Oops! ForegroundService is not allowed", e);
|
||||
}
|
||||
} else
|
||||
{
|
||||
startForeground(DownloaderNotifier.NOTIFICATION_ID, notification);
|
||||
}
|
||||
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public IBinder onBind(Intent intent)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStatusChanged(List<MapManager.StorageCallbackData> data)
|
||||
{
|
||||
var isDownloading = MapManager.nativeIsDownloading();
|
||||
var hasFailed = hasDownloadFailed(data);
|
||||
|
||||
Logger.i(TAG, "Downloading: " + isDownloading + " failure: " + hasFailed);
|
||||
|
||||
if (!isDownloading)
|
||||
{
|
||||
if (hasFailed)
|
||||
{
|
||||
// Detach service from the notification to keep after the service is stopped.
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
|
||||
{
|
||||
stopForeground(Service.STOP_FOREGROUND_DETACH);
|
||||
} else
|
||||
{
|
||||
stopForeground(false);
|
||||
}
|
||||
}
|
||||
stopSelf();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProgress(String countryId, long localSize, long remoteSize)
|
||||
{
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU &&
|
||||
ContextCompat.checkSelfPermission(this, POST_NOTIFICATIONS) != PERMISSION_GRANTED)
|
||||
{
|
||||
Logger.w(TAG, "Permission POST_NOTIFICATIONS is not granted, skipping notification");
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: How to calculate progress?
|
||||
mNotifier.notifyProgress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy()
|
||||
{
|
||||
super.onDestroy();
|
||||
|
||||
Logger.i(TAG, "onDestroy");
|
||||
|
||||
MapManager.nativeUnsubscribe(mSubscriptionSlot);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the foreground service to keep the user informed about the status of region downloads.
|
||||
*/
|
||||
public static void startForegroundService()
|
||||
{
|
||||
Logger.i(TAG);
|
||||
var context = MwmApplication.sInstance;
|
||||
ContextCompat.startForegroundService(context, new Intent(context, DownloaderService.class));
|
||||
}
|
||||
|
||||
private boolean hasDownloadFailed(List<MapManager.StorageCallbackData> data)
|
||||
{
|
||||
for (MapManager.StorageCallbackData item : data)
|
||||
{
|
||||
if (item.isLeafNode && item.newStatus == CountryItem.STATUS_FAILED)
|
||||
{
|
||||
if (MapManager.nativeIsAutoretryFailed())
|
||||
{
|
||||
mNotifier.notifyDownloadFailed(item.countryId);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -58,10 +58,9 @@ class DownloaderToolbarController extends SearchToolbarController
|
|||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecated") // https://github.com/organicmaps/organicmaps/issues/3630
|
||||
protected void startVoiceRecognition(Intent intent, int code)
|
||||
protected void startVoiceRecognition(Intent intent)
|
||||
{
|
||||
mFragment.startActivityForResult(intent, code);
|
||||
mFragment.startVoiceRecognitionForResult.launch(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,28 +1,22 @@
|
|||
package app.organicmaps.downloader;
|
||||
|
||||
import android.app.Application;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import app.organicmaps.util.Utils;
|
||||
|
||||
class ExpandRetryConfirmationListener extends RetryFailedDownloadConfirmationListener
|
||||
class ExpandRetryConfirmationListener implements Runnable
|
||||
{
|
||||
@Nullable
|
||||
private final Utils.Proc<Boolean> mDialogClickListener;
|
||||
|
||||
ExpandRetryConfirmationListener(@NonNull Application app,
|
||||
@Nullable Utils.Proc<Boolean> dialogClickListener)
|
||||
ExpandRetryConfirmationListener(@Nullable Utils.Proc<Boolean> dialogClickListener)
|
||||
{
|
||||
super(app);
|
||||
mDialogClickListener = dialogClickListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
super.run();
|
||||
if (mDialogClickListener == null)
|
||||
return;
|
||||
mDialogClickListener.invoke(true);
|
||||
|
|
|
@ -106,8 +106,8 @@ public final class MapManager
|
|||
})
|
||||
.setPositiveButton(R.string.downloader_retry, (dialog, which) -> {
|
||||
Application app = activity.getApplication();
|
||||
RetryFailedDownloadConfirmationListener listener
|
||||
= new ExpandRetryConfirmationListener(app, dialogClickListener);
|
||||
ExpandRetryConfirmationListener listener
|
||||
= new ExpandRetryConfirmationListener(dialogClickListener);
|
||||
warn3gAndRetry(activity, errorData.countryId, listener);
|
||||
}).create();
|
||||
dlg.setCanceledOnTouchOutside(false);
|
||||
|
@ -208,7 +208,7 @@ public final class MapManager
|
|||
return warnOn3g(activity, countryId, () -> {
|
||||
if (onAcceptListener != null)
|
||||
onAcceptListener.run();
|
||||
nativeDownload(countryId);
|
||||
startDownload(countryId);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -217,10 +217,45 @@ public final class MapManager
|
|||
return warnOn3g(activity, countryId, () -> {
|
||||
if (onAcceptListener != null)
|
||||
onAcceptListener.run();
|
||||
nativeRetry(countryId);
|
||||
retryDownload(countryId);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues failed items under given {@code root} node in downloader.
|
||||
*/
|
||||
public static void retryDownload(@NonNull String countryId) {
|
||||
DownloaderService.startForegroundService();
|
||||
nativeRetry(countryId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues given {@code root} node with its children in downloader.
|
||||
*/
|
||||
public static void startUpdate(@NonNull String root) {
|
||||
DownloaderService.startForegroundService();
|
||||
nativeUpdate(root);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues the given list of nodes and its children in downloader.
|
||||
*/
|
||||
public static void startDownload(String... countries) {
|
||||
DownloaderService.startForegroundService();
|
||||
for (var countryId : countries)
|
||||
{
|
||||
nativeDownload(countryId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues given {@code root} node and its children in downloader.
|
||||
*/
|
||||
public static void startDownload(@NonNull String countryId) {
|
||||
DownloaderService.startForegroundService();
|
||||
nativeDownload(countryId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves ID of root node.
|
||||
*/
|
||||
|
@ -314,17 +349,17 @@ public final class MapManager
|
|||
/**
|
||||
* Enqueues given {@code root} node and its children in downloader.
|
||||
*/
|
||||
public static native void nativeDownload(String root);
|
||||
private static native void nativeDownload(String root);
|
||||
|
||||
/**
|
||||
* Enqueues failed items under given {@code root} node in downloader.
|
||||
*/
|
||||
public static native void nativeRetry(String root);
|
||||
private static native void nativeRetry(String root);
|
||||
|
||||
/**
|
||||
* Enqueues given {@code root} node with its children in downloader.
|
||||
*/
|
||||
public static native void nativeUpdate(String root);
|
||||
private static native void nativeUpdate(String root);
|
||||
|
||||
/**
|
||||
* Removes given currently downloading {@code root} node and its children from downloader.
|
||||
|
|
|
@ -140,8 +140,7 @@ public class OnmapDownloader implements MwmActivity.LeftAnimationTrackListener
|
|||
{
|
||||
mProgress.setPending(false);
|
||||
mProgress.setProgress(Math.round(mCurrentCountry.progress));
|
||||
sizeText = StringUtils.formatUsingSystemLocale("%1$s %2$.2f%%",
|
||||
mActivity.getString(R.string.downloader_downloading), mCurrentCountry.progress);
|
||||
sizeText = mActivity.getString(R.string.downloader_downloading) + " " + StringUtils.formatPercent(mCurrentCountry.progress / 100);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -167,7 +166,7 @@ public class OnmapDownloader implements MwmActivity.LeftAnimationTrackListener
|
|||
if (TextUtils.equals(mCurrentCountry.id, country) &&
|
||||
MapManager.nativeHasSpaceToDownloadCountry(country))
|
||||
{
|
||||
MapManager.nativeDownload(mCurrentCountry.id);
|
||||
MapManager.startDownload(mCurrentCountry.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -211,12 +210,11 @@ public class OnmapDownloader implements MwmActivity.LeftAnimationTrackListener
|
|||
boolean retry = (mCurrentCountry.status == CountryItem.STATUS_FAILED);
|
||||
if (retry)
|
||||
{
|
||||
DownloaderNotifier.cancelNotification(mActivity.getApplicationContext());
|
||||
MapManager.nativeRetry(mCurrentCountry.id);
|
||||
MapManager.retryDownload(mCurrentCountry.id);
|
||||
}
|
||||
else
|
||||
{
|
||||
MapManager.nativeDownload(mCurrentCountry.id);
|
||||
MapManager.startDownload(mCurrentCountry.id);
|
||||
mActivity.requestPostNotificationsPermission();
|
||||
}
|
||||
}));
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
package app.organicmaps.downloader;
|
||||
|
||||
import android.app.Application;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
public class RetryFailedDownloadConfirmationListener implements Runnable
|
||||
{
|
||||
@NonNull
|
||||
private final Application mApplication;
|
||||
|
||||
RetryFailedDownloadConfirmationListener(@NonNull Application application)
|
||||
{
|
||||
mApplication = application;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
DownloaderNotifier.cancelNotification(mApplication);
|
||||
}
|
||||
}
|
|
@ -50,6 +50,8 @@ public class EditorFragment extends BaseMwmFragment implements View.OnClickListe
|
|||
private View mCardName;
|
||||
private View mCardAddress;
|
||||
private View mCardDetails;
|
||||
private View mCardSocialMedia;
|
||||
private View mCardBuilding;
|
||||
|
||||
private RecyclerView mNamesView;
|
||||
|
||||
|
@ -93,7 +95,6 @@ public class EditorFragment extends BaseMwmFragment implements View.OnClickListe
|
|||
|
||||
private TextView mStreet;
|
||||
private TextInputEditText mHouseNumber;
|
||||
private View mBlockLevels;
|
||||
private TextInputEditText mBuildingLevels;
|
||||
|
||||
// Define Metadata entries, that have more tricky logic, separately.
|
||||
|
@ -135,6 +136,7 @@ public class EditorFragment extends BaseMwmFragment implements View.OnClickListe
|
|||
private View mEditOpeningHours;
|
||||
private TextInputEditText mDescription;
|
||||
private final Map<Metadata.MetadataType, View> mDetailsBlocks = new HashMap<>();
|
||||
private final Map<Metadata.MetadataType, View> mSocialMediaBlocks = new HashMap<>();
|
||||
private TextView mReset;
|
||||
|
||||
private EditorHostFragment mParent;
|
||||
|
@ -300,29 +302,29 @@ public class EditorFragment extends BaseMwmFragment implements View.OnClickListe
|
|||
{
|
||||
UiUtils.showIf(Editor.nativeIsNameEditable(), mCardName);
|
||||
UiUtils.showIf(Editor.nativeIsAddressEditable(), mCardAddress);
|
||||
UiUtils.showIf(Editor.nativeIsBuilding() && !Editor.nativeIsPointType(), mBlockLevels);
|
||||
UiUtils.showIf(Editor.nativeIsBuilding() && !Editor.nativeIsPointType(), mCardBuilding);
|
||||
|
||||
final int[] editableDetails = Editor.nativeGetEditableProperties();
|
||||
if (editableDetails.length == 0)
|
||||
{
|
||||
UiUtils.hide(mCardDetails);
|
||||
return;
|
||||
}
|
||||
|
||||
for (var e : mDetailsBlocks.entrySet())
|
||||
setCardVisibility(mCardDetails, mDetailsBlocks, editableDetails);
|
||||
setCardVisibility(mCardSocialMedia, mSocialMediaBlocks, editableDetails);
|
||||
}
|
||||
|
||||
private void setCardVisibility(View card, Map<Metadata. MetadataType, View> blocks, int[] editableDetails) {
|
||||
for (var e : blocks.entrySet())
|
||||
UiUtils.hide(e.getValue());
|
||||
|
||||
boolean anyEditableDetails = false;
|
||||
boolean anyBlockElement = false;
|
||||
for (int type : editableDetails)
|
||||
{
|
||||
final View detailsBlock = mDetailsBlocks.get(Metadata.MetadataType.fromInt(type));
|
||||
if (detailsBlock == null)
|
||||
final View blockElement = blocks.get(Metadata.MetadataType.fromInt(type));
|
||||
if (blockElement == null)
|
||||
continue;
|
||||
|
||||
anyEditableDetails = true;
|
||||
UiUtils.show(detailsBlock);
|
||||
anyBlockElement = true;
|
||||
UiUtils.show(blockElement);
|
||||
}
|
||||
UiUtils.showIf(anyEditableDetails, mCardDetails);
|
||||
UiUtils.showIf(anyBlockElement, card);
|
||||
}
|
||||
|
||||
private void refreshOpeningTime()
|
||||
|
@ -412,20 +414,22 @@ public class EditorFragment extends BaseMwmFragment implements View.OnClickListe
|
|||
mCardName = view.findViewById(R.id.cv__name);
|
||||
mCardAddress = view.findViewById(R.id.cv__address);
|
||||
mCardDetails = view.findViewById(R.id.cv__details);
|
||||
mCardSocialMedia = view.findViewById(R.id.cv__social_media);
|
||||
mCardBuilding = view.findViewById(R.id.cv__building);
|
||||
initNamesView(view);
|
||||
|
||||
// Address
|
||||
view.findViewById(R.id.block_street).setOnClickListener(this);
|
||||
mStreet = view.findViewById(R.id.street);
|
||||
View blockHouseNumber = view.findViewById(R.id.block_building);
|
||||
mHouseNumber = findInputAndInitBlock(blockHouseNumber, 0, R.string.house_number);
|
||||
mHouseNumber = findInputAndInitBlock(blockHouseNumber, R.drawable.ic_building, R.string.house_number);
|
||||
mInputHouseNumber = blockHouseNumber.findViewById(R.id.custom_input);
|
||||
|
||||
initBlock(view, Metadata.MetadataType.FMD_POSTCODE, R.id.block_zipcode, 0, R.string.editor_zip_code, 0);
|
||||
initBlock(view, Metadata.MetadataType.FMD_POSTCODE, R.id.block_zipcode, R.drawable.ic_address, R.string.editor_zip_code, 0);
|
||||
|
||||
// Details
|
||||
mBlockLevels = view.findViewById(R.id.block_levels);
|
||||
mBuildingLevels = findInputAndInitBlock(mBlockLevels, 0,
|
||||
View mBlockLevels = view.findViewById(R.id.block_levels);
|
||||
mBuildingLevels = findInputAndInitBlock(mBlockLevels, R.drawable.ic_floor,
|
||||
getString(R.string.editor_storey_number, Editor.nativeGetMaxEditableBuildingLevels()));
|
||||
mBuildingLevels.setInputType(InputType.TYPE_CLASS_NUMBER);
|
||||
mInputBuildingLevels = mBlockLevels.findViewById(R.id.custom_input);
|
||||
|
@ -493,12 +497,13 @@ public class EditorFragment extends BaseMwmFragment implements View.OnClickListe
|
|||
mDetailsBlocks.put(Metadata.MetadataType.FMD_WEBSITE, websiteBlock);
|
||||
mDetailsBlocks.put(Metadata.MetadataType.FMD_WEBSITE_MENU, websiteMenuBlock);
|
||||
mDetailsBlocks.put(Metadata.MetadataType.FMD_EMAIL, emailBlock);
|
||||
mDetailsBlocks.put(Metadata.MetadataType.FMD_CONTACT_FACEBOOK, facebookContactBlock);
|
||||
mDetailsBlocks.put(Metadata.MetadataType.FMD_CONTACT_INSTAGRAM, instagramContactBlock);
|
||||
mDetailsBlocks.put(Metadata.MetadataType.FMD_CONTACT_TWITTER, twitterContactBlock);
|
||||
mDetailsBlocks.put(Metadata.MetadataType.FMD_CONTACT_VK, vkContactBlock);
|
||||
mDetailsBlocks.put(Metadata.MetadataType.FMD_CONTACT_LINE, lineContactBlock);
|
||||
mDetailsBlocks.put(Metadata.MetadataType.FMD_OPERATOR, operatorBlock);
|
||||
|
||||
mSocialMediaBlocks.put(Metadata.MetadataType.FMD_CONTACT_FACEBOOK, facebookContactBlock);
|
||||
mSocialMediaBlocks.put(Metadata.MetadataType.FMD_CONTACT_INSTAGRAM, instagramContactBlock);
|
||||
mSocialMediaBlocks.put(Metadata.MetadataType.FMD_CONTACT_TWITTER, twitterContactBlock);
|
||||
mSocialMediaBlocks.put(Metadata.MetadataType.FMD_CONTACT_VK, vkContactBlock);
|
||||
mSocialMediaBlocks.put(Metadata.MetadataType.FMD_CONTACT_LINE, lineContactBlock);
|
||||
}
|
||||
|
||||
private static TextInputEditText findInput(View blockWithInput)
|
||||
|
|
|
@ -350,7 +350,7 @@ public class EditorHostFragment extends BaseMwmToolbarFragment implements View.O
|
|||
private void processEditedFeatures()
|
||||
{
|
||||
Context context = requireContext();
|
||||
if (OsmOAuth.isAuthorized(context) || !ConnectionState.INSTANCE.isConnected())
|
||||
if (OsmOAuth.isAuthorized(context))
|
||||
{
|
||||
Utils.navigateToParent(requireActivity());
|
||||
return;
|
||||
|
|
|
@ -3,7 +3,6 @@ package app.organicmaps.intent;
|
|||
import android.content.ContentResolver;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.IntentCompat;
|
||||
|
@ -22,7 +21,7 @@ import app.organicmaps.bookmarks.data.MapObject;
|
|||
import app.organicmaps.editor.OsmLoginActivity;
|
||||
import app.organicmaps.routing.RoutingController;
|
||||
import app.organicmaps.search.SearchActivity;
|
||||
import app.organicmaps.search.SearchEngine;
|
||||
import app.organicmaps.sdk.search.SearchEngine;
|
||||
import app.organicmaps.util.StorageUtils;
|
||||
import app.organicmaps.util.concurrency.ThreadPool;
|
||||
|
||||
|
@ -99,7 +98,7 @@ public class Factory
|
|||
RoutingController.get().prepare(MapObject.createMapObject(FeatureId.EMPTY, MapObject.API_POINT,
|
||||
from.mName, "", from.mLat, from.mLon),
|
||||
MapObject.createMapObject(FeatureId.EMPTY, MapObject.API_POINT,
|
||||
to.mName, "", to.mLat, to.mLon), true);
|
||||
to.mName, "", to.mLat, to.mLon));
|
||||
return true;
|
||||
case RequestType.SEARCH:
|
||||
{
|
||||
|
|
|
@ -200,6 +200,8 @@ public class TrackRecordingService extends Service implements LocationListener
|
|||
.setSmallIcon(R.drawable.warning_icon)
|
||||
.setContentTitle(context.getString(R.string.current_location_unknown_error_title))
|
||||
.setContentText(context.getString(R.string.dialog_routing_location_turn_wifi))
|
||||
.setStyle(new NotificationCompat.BigTextStyle()
|
||||
.bigText(context.getString(R.string.dialog_routing_location_turn_wifi)))
|
||||
.addAction(0, context.getString(R.string.navigation_stop_button), getExitPendingIntent(context))
|
||||
.setContentIntent(getPendingIntent(context))
|
||||
.setColor(ContextCompat.getColor(context, R.color.notification_warning));
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
package app.organicmaps.maplayer;
|
||||
|
||||
import android.animation.ArgbEvaluator;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.util.TypedValue;
|
||||
|
@ -52,6 +55,8 @@ public class MapButtonsController extends Fragment
|
|||
private View mBottomButtonsFrame;
|
||||
@Nullable
|
||||
private LayersButton mToggleMapLayerButton;
|
||||
@Nullable
|
||||
FloatingActionButton mTrackRecordingStatusButton;
|
||||
|
||||
@Nullable
|
||||
private MyPositionButton mNavMyPosition;
|
||||
|
@ -68,7 +73,11 @@ public class MapButtonsController extends Fragment
|
|||
private final Observer<Boolean> mButtonHiddenObserver = this::setButtonsHidden;
|
||||
private final Observer<Integer> mMyPositionModeObserver = this::updateNavMyPositionButton;
|
||||
private final Observer<SearchWheel.SearchOption> mSearchOptionObserver = this::onSearchOptionChange;
|
||||
private final Observer<Boolean> mTrackRecorderObserver = this::updateMenuBadge;
|
||||
private final Observer<Boolean> mTrackRecorderObserver = (enable) -> {
|
||||
updateMenuBadge(enable);
|
||||
showButton(enable, MapButtons.trackRecordingStatus);
|
||||
};
|
||||
private final Observer<Integer> mTopButtonMarginObserver = this::updateTopButtonsMargin;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
|
@ -119,6 +128,10 @@ public class MapButtonsController extends Fragment
|
|||
mToggleMapLayerButton.setOnClickListener(view -> mMapButtonClickListener.onMapButtonClick(MapButtons.toggleMapLayer));
|
||||
mToggleMapLayerButton.setVisibility(View.VISIBLE);
|
||||
}
|
||||
mMapButtonsViewModel.setTopButtonsMarginTop(-1);
|
||||
mTrackRecordingStatusButton = mFrame.findViewById(R.id.track_recording_status);
|
||||
if (mTrackRecordingStatusButton != null)
|
||||
mTrackRecordingStatusButton.setOnClickListener(view -> mMapButtonClickListener.onMapButtonClick(MapButtons.trackRecordingStatus));
|
||||
final View menuButton = mFrame.findViewById(R.id.menu_button);
|
||||
if (menuButton != null)
|
||||
{
|
||||
|
@ -157,6 +170,9 @@ public class MapButtonsController extends Fragment
|
|||
mButtonsMap.put(MapButtons.menu, menuButton);
|
||||
if (helpButton != null)
|
||||
mButtonsMap.put(MapButtons.help, helpButton);
|
||||
if (mTrackRecordingStatusButton != null)
|
||||
mButtonsMap.put(MapButtons.trackRecordingStatus, mTrackRecordingStatusButton);
|
||||
showButton(false, MapButtons.trackRecordingStatus);
|
||||
return mFrame;
|
||||
}
|
||||
|
||||
|
@ -184,6 +200,28 @@ public class MapButtonsController extends Fragment
|
|||
case bookmarks:
|
||||
case menu:
|
||||
UiUtils.showIf(show, buttonView);
|
||||
break;
|
||||
case trackRecordingStatus:
|
||||
UiUtils.showIf(show, buttonView);
|
||||
animateIconBlinking(show, (FloatingActionButton) buttonView);
|
||||
}
|
||||
}
|
||||
|
||||
void animateIconBlinking(boolean show, @NonNull FloatingActionButton button)
|
||||
{
|
||||
if (show)
|
||||
{
|
||||
Drawable drawable = button.getDrawable();
|
||||
ObjectAnimator colorAnimator = ObjectAnimator.ofArgb(
|
||||
drawable,
|
||||
"tint",
|
||||
0xFF757575,
|
||||
0xFFFF0000);
|
||||
colorAnimator.setDuration(2500);
|
||||
colorAnimator.setEvaluator(new ArgbEvaluator());
|
||||
colorAnimator.setRepeatCount(ObjectAnimator.INFINITE);
|
||||
colorAnimator.setRepeatMode(ObjectAnimator.REVERSE);
|
||||
colorAnimator.start();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -192,6 +230,15 @@ public class MapButtonsController extends Fragment
|
|||
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, context.getResources().getDisplayMetrics());
|
||||
}
|
||||
|
||||
private void updateTopButtonsMargin(int margin)
|
||||
{
|
||||
if (margin == -1 || mTrackRecordingStatusButton == null)
|
||||
return;
|
||||
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) mTrackRecordingStatusButton.getLayoutParams();
|
||||
params.topMargin = margin;
|
||||
mTrackRecordingStatusButton.setLayoutParams(params);
|
||||
}
|
||||
|
||||
@OptIn(markerClass = ExperimentalBadgeUtils.class)
|
||||
private void updateMenuBadge(Boolean enable)
|
||||
{
|
||||
|
@ -352,6 +399,7 @@ public class MapButtonsController extends Fragment
|
|||
mMapButtonsViewModel.getMyPositionMode().observe(activity, mMyPositionModeObserver);
|
||||
mMapButtonsViewModel.getSearchOption().observe(activity, mSearchOptionObserver);
|
||||
mMapButtonsViewModel.getTrackRecorderState().observe(activity, mTrackRecorderObserver);
|
||||
mMapButtonsViewModel.getTopButtonsMarginTop().observe(activity, mTopButtonMarginObserver);
|
||||
}
|
||||
|
||||
public void onResume()
|
||||
|
@ -378,6 +426,7 @@ public class MapButtonsController extends Fragment
|
|||
public void onStop()
|
||||
{
|
||||
super.onStop();
|
||||
mMapButtonsViewModel.getTopButtonsMarginTop().removeObserver(mTopButtonMarginObserver);
|
||||
mPlacePageViewModel.getPlacePageDistanceToTop().removeObserver(mPlacePageDistanceToTopObserver);
|
||||
mMapButtonsViewModel.getButtonsHidden().removeObserver(mButtonHiddenObserver);
|
||||
mMapButtonsViewModel.getMyPositionMode().removeObserver(mMyPositionModeObserver);
|
||||
|
@ -407,7 +456,8 @@ public class MapButtonsController extends Fragment
|
|||
search,
|
||||
bookmarks,
|
||||
menu,
|
||||
help
|
||||
help,
|
||||
trackRecordingStatus
|
||||
}
|
||||
|
||||
public interface MapButtonClickListener
|
||||
|
|
|
@ -9,6 +9,7 @@ public class MapButtonsViewModel extends ViewModel
|
|||
{
|
||||
private final MutableLiveData<Boolean> mButtonsHidden = new MutableLiveData<>(false);
|
||||
private final MutableLiveData<Float> mBottomButtonsHeight = new MutableLiveData<>(0f);
|
||||
private final MutableLiveData<Integer> mTopButtonsMarginTop = new MutableLiveData<>(-1);
|
||||
private final MutableLiveData<MapButtonsController.LayoutMode> mLayoutMode = new MutableLiveData<>(MapButtonsController.LayoutMode.regular);
|
||||
private final MutableLiveData<Integer> mMyPositionMode = new MutableLiveData<>();
|
||||
private final MutableLiveData<SearchWheel.SearchOption> mSearchOption = new MutableLiveData<>();
|
||||
|
@ -34,6 +35,16 @@ public class MapButtonsViewModel extends ViewModel
|
|||
mBottomButtonsHeight.setValue(height);
|
||||
}
|
||||
|
||||
public MutableLiveData<Integer> getTopButtonsMarginTop()
|
||||
{
|
||||
return mTopButtonsMarginTop;
|
||||
}
|
||||
|
||||
public void setTopButtonsMarginTop(int margin)
|
||||
{
|
||||
mTopButtonsMarginTop.setValue(margin);
|
||||
}
|
||||
|
||||
public MutableLiveData<MapButtonsController.LayoutMode> getLayoutMode()
|
||||
{
|
||||
return mLayoutMode;
|
||||
|
|
|
@ -16,7 +16,7 @@ import androidx.annotation.Nullable;
|
|||
import androidx.annotation.StringRes;
|
||||
import app.organicmaps.R;
|
||||
import app.organicmaps.routing.RoutingController;
|
||||
import app.organicmaps.search.SearchEngine;
|
||||
import app.organicmaps.sdk.search.SearchEngine;
|
||||
import app.organicmaps.util.Graphics;
|
||||
import app.organicmaps.util.UiUtils;
|
||||
import app.organicmaps.util.concurrency.UiThread;
|
||||
|
|
|
@ -0,0 +1,265 @@
|
|||
package app.organicmaps.routing;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.RectF;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.content.res.AppCompatResources;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import app.organicmaps.R;
|
||||
import app.organicmaps.bookmarks.data.MapObject;
|
||||
import app.organicmaps.util.StringUtils;
|
||||
import app.organicmaps.util.UiUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
public class ManageRouteAdapter extends RecyclerView.Adapter<ManageRouteAdapter.ManageRouteViewHolder>
|
||||
{
|
||||
Context mContext;
|
||||
ArrayList<RouteMarkData> mRoutePoints;
|
||||
ManageRouteListener mManageRouteListener;
|
||||
|
||||
public interface ManageRouteListener
|
||||
{
|
||||
void startDrag(RecyclerView.ViewHolder viewHolder);
|
||||
void showMyLocationIcon(boolean showMyLocationIcon);
|
||||
void onRoutePointDeleted(RecyclerView.ViewHolder viewHolder);
|
||||
}
|
||||
|
||||
public ManageRouteAdapter(Context context, RouteMarkData[] routeMarkData, ManageRouteListener listener)
|
||||
{
|
||||
mContext = context;
|
||||
mRoutePoints = new ArrayList<>(Arrays.asList(routeMarkData));
|
||||
mManageRouteListener = listener;
|
||||
|
||||
updateMyLocationIcon();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ManageRouteViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType)
|
||||
{
|
||||
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.manage_route_list_item,
|
||||
parent, false);
|
||||
|
||||
return new ManageRouteViewHolder(view);
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ManageRouteViewHolder holder, int position)
|
||||
{
|
||||
// Set route point icon.
|
||||
int iconId;
|
||||
|
||||
switch (mRoutePoints.get(position).mPointType)
|
||||
{
|
||||
case RoutePointInfo.ROUTE_MARK_START: // Starting point.
|
||||
if (mRoutePoints.get(position).mIsMyPosition)
|
||||
iconId = R.drawable.ic_location_arrow_blue;
|
||||
else
|
||||
iconId = R.drawable.route_point_start;
|
||||
break;
|
||||
|
||||
case RoutePointInfo.ROUTE_MARK_INTERMEDIATE: // Intermediate stop.
|
||||
TypedArray iconArray = mContext.getResources().obtainTypedArray(R.array.route_stop_icons);
|
||||
iconId = iconArray.getResourceId(mRoutePoints.get(position).mIntermediateIndex,
|
||||
R.drawable.route_point_20);
|
||||
iconArray.recycle();
|
||||
break;
|
||||
|
||||
case RoutePointInfo.ROUTE_MARK_FINISH: // Destination point.
|
||||
iconId = R.drawable.route_point_finish;
|
||||
break;
|
||||
|
||||
default: // Unknown route type.
|
||||
iconId = R.drawable.warning_icon;
|
||||
break;
|
||||
}
|
||||
|
||||
// Set icon widget.
|
||||
holder.mImageViewIcon.setImageDrawable(AppCompatResources.getDrawable(mContext, iconId));
|
||||
|
||||
// Set title & subtitle.
|
||||
String title, subtitle;
|
||||
|
||||
if (mRoutePoints.get(position).mIsMyPosition)
|
||||
{
|
||||
// My position point.
|
||||
title = mContext.getString(R.string.core_my_position);
|
||||
|
||||
if (mRoutePoints.get(position).mPointType != RoutePointInfo.ROUTE_MARK_START)
|
||||
subtitle = mRoutePoints.get(position).mTitle;
|
||||
else
|
||||
{
|
||||
// Hide my position coordinates if it's the starting point of the route.
|
||||
subtitle = "";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
title = mRoutePoints.get(position).mTitle;
|
||||
subtitle = mRoutePoints.get(position).mSubtitle;
|
||||
}
|
||||
|
||||
holder.mTextViewTitle.setText(title);
|
||||
holder.mTextViewSubtitle.setText(subtitle);
|
||||
UiUtils.showIf(subtitle != null && !subtitle.isEmpty(), holder.mTextViewSubtitle);
|
||||
|
||||
// Show 'Delete' icon button only if we have intermediate stops.
|
||||
UiUtils.showIf(mRoutePoints.size() > 2, holder.mImageViewDelete);
|
||||
|
||||
// Detection of touch events on holder view.
|
||||
holder.mItemView.setOnTouchListener((v, event) -> {
|
||||
|
||||
if (event.getAction() == MotionEvent.ACTION_DOWN)
|
||||
{
|
||||
RectF deleteButtonRect = new RectF(holder.mImageViewDelete.getLeft(),
|
||||
holder.mImageViewDelete.getTop(),
|
||||
holder.mImageViewDelete.getRight(),
|
||||
holder.mImageViewDelete.getBottom());
|
||||
|
||||
if (holder.mImageViewDelete.isShown() && deleteButtonRect.contains(event.getX(), event.getY()))
|
||||
{
|
||||
// User has clicked on the 'Delete' icon button.
|
||||
mManageRouteListener.onRoutePointDeleted(holder);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Call start drag listener on touch.
|
||||
mManageRouteListener.startDrag(holder);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount()
|
||||
{
|
||||
return mRoutePoints.size();
|
||||
}
|
||||
|
||||
public void moveRoutePoint(int draggedItemIndex, int targetIndex)
|
||||
{
|
||||
if (draggedItemIndex == targetIndex) // Dragged to same spot. Do nothing.
|
||||
return;
|
||||
|
||||
Collections.swap(mRoutePoints, draggedItemIndex, targetIndex);
|
||||
|
||||
updateRoutePointsData();
|
||||
|
||||
notifyItemMoved(draggedItemIndex, targetIndex);
|
||||
}
|
||||
|
||||
public void deleteRoutePoint(RecyclerView.ViewHolder viewHolder)
|
||||
{
|
||||
mRoutePoints.remove(viewHolder.getAbsoluteAdapterPosition());
|
||||
|
||||
updateRoutePointsData();
|
||||
|
||||
notifyItemRemoved(viewHolder.getAbsoluteAdapterPosition());
|
||||
}
|
||||
|
||||
public void setMyLocationAsStartingPoint(MapObject myLocation)
|
||||
{
|
||||
String latLonString = StringUtils.formatUsingUsLocale("%.6f, %.6f",
|
||||
myLocation.getLat(),
|
||||
myLocation.getLon());
|
||||
|
||||
// Replace route point in first position with 'My Position".
|
||||
mRoutePoints.set(0, new RouteMarkData(latLonString, "", RoutePointInfo.ROUTE_MARK_START,
|
||||
0, true, true, false, myLocation.getLat(),
|
||||
myLocation.getLon()));
|
||||
|
||||
// Update data.
|
||||
updateRoutePointsData();
|
||||
|
||||
// Update adapter.
|
||||
notifyItemChanged(0);
|
||||
|
||||
// Show 'My location' crosshair button.
|
||||
if (mManageRouteListener != null)
|
||||
mManageRouteListener.showMyLocationIcon(true);
|
||||
}
|
||||
|
||||
private void updateMyLocationIcon()
|
||||
{
|
||||
boolean containsMyLocationPoint = false;
|
||||
|
||||
for (RouteMarkData routePoint : mRoutePoints)
|
||||
{
|
||||
if (routePoint.mIsMyPosition)
|
||||
{
|
||||
containsMyLocationPoint = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (mManageRouteListener != null)
|
||||
mManageRouteListener.showMyLocationIcon(!containsMyLocationPoint);
|
||||
}
|
||||
|
||||
private void updateRoutePointsData()
|
||||
{
|
||||
assert(mRoutePoints.size() >= 2);
|
||||
|
||||
// Set starting point.
|
||||
mRoutePoints.get(0).mPointType = RoutePointInfo.ROUTE_MARK_START;
|
||||
|
||||
// Set finish point.
|
||||
mRoutePoints.get(mRoutePoints.size() - 1).mPointType = RoutePointInfo.ROUTE_MARK_FINISH;
|
||||
|
||||
// Set intermediate point(s).
|
||||
for (int pos = 1; pos < mRoutePoints.size() - 1; pos++)
|
||||
{
|
||||
mRoutePoints.get(pos).mPointType = RoutePointInfo.ROUTE_MARK_INTERMEDIATE;
|
||||
mRoutePoints.get(pos).mIntermediateIndex = pos - 1;
|
||||
}
|
||||
}
|
||||
|
||||
public ArrayList<RouteMarkData> getRoutePoints()
|
||||
{
|
||||
return mRoutePoints;
|
||||
}
|
||||
|
||||
static class ManageRouteViewHolder extends RecyclerView.ViewHolder
|
||||
{
|
||||
@NonNull
|
||||
public final View mItemView;
|
||||
|
||||
@NonNull
|
||||
public final ImageView mImageViewIcon;
|
||||
|
||||
@NonNull
|
||||
public final TextView mTextViewTitle;
|
||||
|
||||
@NonNull
|
||||
public final TextView mTextViewSubtitle;
|
||||
|
||||
@NonNull
|
||||
public final ImageView mImageViewDelete;
|
||||
|
||||
ManageRouteViewHolder(@NonNull View itemView)
|
||||
{
|
||||
super(itemView);
|
||||
mItemView = itemView;
|
||||
mImageViewIcon = itemView.findViewById(R.id.type_icon);
|
||||
mTextViewTitle = itemView.findViewById(R.id.title);
|
||||
mTextViewSubtitle = itemView.findViewById(R.id.subtitle);
|
||||
mImageViewDelete = itemView.findViewById(R.id.delete_icon);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,266 @@
|
|||
package app.organicmaps.routing;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Bundle;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import app.organicmaps.Framework;
|
||||
import app.organicmaps.MwmApplication;
|
||||
import app.organicmaps.R;
|
||||
import app.organicmaps.bookmarks.data.MapObject;
|
||||
import app.organicmaps.util.UiUtils;
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior;
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog;
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
|
||||
import com.google.android.material.divider.MaterialDividerItemDecoration;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import static androidx.recyclerview.widget.ItemTouchHelper.*;
|
||||
|
||||
public class ManageRouteBottomSheet extends BottomSheetDialogFragment
|
||||
implements View.OnClickListener, ManageRouteAdapter.ManageRouteListener
|
||||
{
|
||||
ManageRouteAdapter mManageRouteAdapter;
|
||||
ItemTouchHelper mTouchHelper;
|
||||
ImageView mMyLocationImageView;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState)
|
||||
{
|
||||
View v = inflater.inflate(R.layout.manage_route_bottom_sheet, container, false);
|
||||
|
||||
Button cancelButton = v.findViewById(R.id.btn__cancel);
|
||||
cancelButton.setOnClickListener(this);
|
||||
|
||||
Button planButton = v.findViewById(R.id.btn__plan);
|
||||
planButton.setOnClickListener(this);
|
||||
|
||||
mMyLocationImageView = v.findViewById(R.id.image_my_location);
|
||||
mMyLocationImageView.setOnClickListener(this);
|
||||
|
||||
RecyclerView manageRouteList = v.findViewById(R.id.manage_route_list);
|
||||
LinearLayoutManager layoutManager = new LinearLayoutManager(getContext());
|
||||
manageRouteList.setLayoutManager(layoutManager);
|
||||
RecyclerView.ItemDecoration decoration = new MaterialDividerItemDecoration(getContext(),
|
||||
layoutManager.getOrientation());
|
||||
manageRouteList.addItemDecoration(decoration);
|
||||
|
||||
mManageRouteAdapter = new ManageRouteAdapter(getContext(), Framework.nativeGetRoutePoints(), this);
|
||||
|
||||
manageRouteList.setAdapter(mManageRouteAdapter);
|
||||
|
||||
// Enable drag & drop in route list.
|
||||
mTouchHelper = new ItemTouchHelper(new ManageRouteItemTouchHelperCallback(mManageRouteAdapter,
|
||||
getResources()));
|
||||
mTouchHelper.attachToRecyclerView(manageRouteList);
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState)
|
||||
{
|
||||
Dialog dialog = super.onCreateDialog(savedInstanceState);
|
||||
|
||||
// Expand bottom sheet dialog.
|
||||
dialog.setOnShowListener(dialogInterface -> {
|
||||
|
||||
FrameLayout bottomSheet = ((BottomSheetDialog) dialogInterface).findViewById(
|
||||
com.google.android.material.R.id.design_bottom_sheet);
|
||||
|
||||
if (bottomSheet != null)
|
||||
BottomSheetBehavior.from(bottomSheet).setState(BottomSheetBehavior.STATE_EXPANDED);
|
||||
});
|
||||
|
||||
// Set key listener to detect back button pressed.
|
||||
dialog.setOnKeyListener((dialog1, keyCode, event) -> {
|
||||
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP)
|
||||
{
|
||||
// Dismiss the fragment
|
||||
dismiss();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Otherwise, do nothing else.
|
||||
return false;
|
||||
});
|
||||
|
||||
return dialog;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v)
|
||||
{
|
||||
int buttonId = v.getId();
|
||||
|
||||
if (buttonId == R.id.btn__cancel)
|
||||
{
|
||||
// Close dialog if 'Cancel' button is pressed.
|
||||
dismiss();
|
||||
}
|
||||
else if (buttonId == R.id.image_my_location)
|
||||
{
|
||||
// Get current location.
|
||||
MapObject myLocation = MwmApplication.from(getContext()).getLocationHelper().getMyPosition();
|
||||
|
||||
// Set 'My Location' as starting point of the route.
|
||||
if (myLocation != null)
|
||||
mManageRouteAdapter.setMyLocationAsStartingPoint(myLocation);
|
||||
}
|
||||
else if (buttonId == R.id.btn__plan)
|
||||
{
|
||||
// Get route points from adapter.
|
||||
ArrayList<RouteMarkData> newRoutePoints = mManageRouteAdapter.getRoutePoints();
|
||||
|
||||
// Make sure that the new route contains at least 2 points (start and destination).
|
||||
assert(newRoutePoints.size() >= 2);
|
||||
|
||||
// Remove all existing route points.
|
||||
Framework.nativeRemoveRoutePoints();
|
||||
|
||||
// First, add the destination point.
|
||||
Framework.addRoutePoint(newRoutePoints.get(newRoutePoints.size() - 1));
|
||||
|
||||
// Secondly, add the starting point.
|
||||
Framework.addRoutePoint(newRoutePoints.get(0));
|
||||
|
||||
// And then, add all intermediate points.
|
||||
for (int pos = 1; pos < newRoutePoints.size() - 1; pos++)
|
||||
Framework.addRoutePoint(newRoutePoints.get(pos));
|
||||
|
||||
// Intermediate route points are added sorted by distance.
|
||||
// We have to make sure that they follow the requested order.
|
||||
RouteMarkData[] finalRoutePoints = Framework.nativeGetRoutePoints();
|
||||
|
||||
for (int first = 1; first < newRoutePoints.size() - 1; first++)
|
||||
{
|
||||
int secondIndex = -1;
|
||||
|
||||
for (int second = first; second < newRoutePoints.size() - 1; second++)
|
||||
{
|
||||
if (finalRoutePoints[first].equals(newRoutePoints.get(second)))
|
||||
{
|
||||
secondIndex = second;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (secondIndex < 0)
|
||||
{
|
||||
// Something went bad. Intermediate point not found in the route points.
|
||||
break;
|
||||
}
|
||||
|
||||
if (first != secondIndex)
|
||||
{
|
||||
// Intermediate point needs to be moved.
|
||||
Framework.nativeMoveRoutePoint(secondIndex, first);
|
||||
|
||||
// Refresh final route points.
|
||||
finalRoutePoints = Framework.nativeGetRoutePoints();
|
||||
}
|
||||
}
|
||||
|
||||
// Launch route planning.
|
||||
RoutingController.get().launchPlanning();
|
||||
|
||||
// Dismiss (close) manage route bottom sheet.
|
||||
dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startDrag(RecyclerView.ViewHolder viewHolder)
|
||||
{
|
||||
// Start dragging.
|
||||
mTouchHelper.startDrag(viewHolder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showMyLocationIcon(boolean showMyLocationIcon)
|
||||
{
|
||||
// Get current location.
|
||||
MapObject myLocation = MwmApplication.from(getContext()).getLocationHelper().getMyPosition();
|
||||
|
||||
UiUtils.showIf(showMyLocationIcon && myLocation != null, mMyLocationImageView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRoutePointDeleted(RecyclerView.ViewHolder viewHolder)
|
||||
{
|
||||
mManageRouteAdapter.deleteRoutePoint(viewHolder);
|
||||
|
||||
mManageRouteAdapter.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
private static class ManageRouteItemTouchHelperCallback extends ItemTouchHelper.Callback
|
||||
{
|
||||
private final ManageRouteAdapter mManageRouteAdapter;
|
||||
|
||||
public ManageRouteItemTouchHelperCallback(ManageRouteAdapter adapter, Resources resources)
|
||||
{
|
||||
mManageRouteAdapter = adapter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMovementFlags(@NonNull RecyclerView recyclerView,
|
||||
@NonNull RecyclerView.ViewHolder viewHolder)
|
||||
{
|
||||
// Enable up & down dragging. No left-right swiping is enabled.
|
||||
return makeMovementFlags(UP | DOWN, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLongPressDragEnabled()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isItemViewSwipeEnabled()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMove(@NonNull RecyclerView recyclerView,
|
||||
@NonNull RecyclerView.ViewHolder viewHolder,
|
||||
@NonNull RecyclerView.ViewHolder target)
|
||||
{
|
||||
mManageRouteAdapter.moveRoutePoint(viewHolder.getAbsoluteAdapterPosition(),
|
||||
target.getAbsoluteAdapterPosition());
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction)
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearView(@NonNull RecyclerView recyclerView,
|
||||
@NonNull RecyclerView.ViewHolder viewHolder)
|
||||
{
|
||||
super.clearView(recyclerView, viewHolder);
|
||||
|
||||
// Called when dragging action has finished.
|
||||
mManageRouteAdapter.notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,15 +12,18 @@ import androidx.appcompat.app.AppCompatActivity;
|
|||
import androidx.core.graphics.Insets;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.core.view.WindowInsetsCompat;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import app.organicmaps.Framework;
|
||||
import app.organicmaps.R;
|
||||
import app.organicmaps.location.LocationHelper;
|
||||
import app.organicmaps.maplayer.MapButtonsViewModel;
|
||||
import app.organicmaps.maplayer.traffic.TrafficManager;
|
||||
import app.organicmaps.util.StringUtils;
|
||||
import app.organicmaps.util.UiUtils;
|
||||
import app.organicmaps.util.Utils;
|
||||
import app.organicmaps.util.WindowInsetUtils;
|
||||
import app.organicmaps.widget.LanesView;
|
||||
import app.organicmaps.widget.SpeedLimitView;
|
||||
import app.organicmaps.util.WindowInsetUtils;
|
||||
import app.organicmaps.widget.menu.NavMenu;
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior;
|
||||
|
||||
|
@ -44,6 +47,8 @@ public class NavigationController implements TrafficManager.TrafficCallback,
|
|||
@NonNull
|
||||
private final SpeedLimitView mSpeedLimit;
|
||||
|
||||
private final MapButtonsViewModel mMapButtonsViewModel;
|
||||
|
||||
private final NavMenu mNavMenu;
|
||||
View.OnClickListener mOnSettingsClickListener;
|
||||
|
||||
|
@ -59,6 +64,8 @@ public class NavigationController implements TrafficManager.TrafficCallback,
|
|||
public NavigationController(AppCompatActivity activity, View.OnClickListener onSettingsClickListener,
|
||||
NavMenu.OnMenuSizeChangedListener onMenuSizeChangedListener)
|
||||
{
|
||||
mMapButtonsViewModel = new ViewModelProvider(activity).get(MapButtonsViewModel.class);
|
||||
|
||||
mFrame = activity.findViewById(R.id.navigation_frame);
|
||||
mNavMenu = new NavMenu(activity, this, onMenuSizeChangedListener);
|
||||
mOnSettingsClickListener = onSettingsClickListener;
|
||||
|
@ -156,6 +163,10 @@ public class NavigationController implements TrafficManager.TrafficCallback,
|
|||
UiUtils.visibleIf(hasStreet, mStreetFrame);
|
||||
if (!TextUtils.isEmpty(info.nextStreet))
|
||||
mNextStreet.setText(info.nextStreet);
|
||||
int margin = UiUtils.dimen(mFrame.getContext(), R.dimen.nav_frame_padding);
|
||||
if (hasStreet)
|
||||
margin += mStreetFrame.getHeight();
|
||||
mMapButtonsViewModel.setTopButtonsMarginTop(margin);
|
||||
}
|
||||
|
||||
public void show(boolean show)
|
||||
|
@ -249,10 +260,10 @@ public class NavigationController implements TrafficManager.TrafficCallback,
|
|||
{
|
||||
final Location location = LocationHelper.from(mFrame.getContext()).getSavedLocation();
|
||||
if (location == null) {
|
||||
mSpeedLimit.setSpeedLimitMps(0);
|
||||
mSpeedLimit.setSpeedLimit(0, false);
|
||||
return;
|
||||
}
|
||||
mSpeedLimit.setCurrentSpeed(location.getSpeed());
|
||||
mSpeedLimit.setSpeedLimitMps(info.speedLimitMps);
|
||||
final boolean speedLimitExceeded = info.speedLimitMps < location.getSpeed();
|
||||
mSpeedLimit.setSpeedLimit(StringUtils.nativeFormatSpeed(info.speedLimitMps), speedLimitExceeded);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,8 +16,8 @@ public class RouteMarkData
|
|||
@Nullable
|
||||
public final String mSubtitle;
|
||||
@RoutePointInfo.RouteMarkType
|
||||
public final int mPointType;
|
||||
public final int mIntermediateIndex;
|
||||
public int mPointType;
|
||||
public int mIntermediateIndex;
|
||||
public final boolean mIsVisible;
|
||||
public final boolean mIsMyPosition;
|
||||
public final boolean mIsPassed;
|
||||
|
@ -39,4 +39,12 @@ public class RouteMarkData
|
|||
mLat = lat;
|
||||
mLon = lon;
|
||||
}
|
||||
|
||||
public boolean equals(RouteMarkData other)
|
||||
{
|
||||
return mTitle != null && other.mTitle != null &&
|
||||
mTitle.compareTo(other.mTitle) == 0 &&
|
||||
mSubtitle != null && other.mSubtitle != null &&
|
||||
mSubtitle.compareTo(other.mSubtitle) == 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -151,6 +151,8 @@ final class RoutingBottomMenuController implements View.OnClickListener
|
|||
Resources res = mContext.getResources();
|
||||
mTransitViewDecorator = new DotDividerItemDecoration(dividerDrawable, res.getDimensionPixelSize(R.dimen.margin_base),
|
||||
res.getDimensionPixelSize(R.dimen.margin_half));
|
||||
Button manageRouteButton = altitudeChartFrame.findViewById(R.id.btn__manage_route);
|
||||
manageRouteButton.setOnClickListener(this);
|
||||
}
|
||||
|
||||
void showAltitudeChartAndRoutingDetails()
|
||||
|
@ -253,7 +255,7 @@ final class RoutingBottomMenuController implements View.OnClickListener
|
|||
if (LocationHelper.from(mContext).getMyPosition() != null)
|
||||
{
|
||||
UiUtils.show(mActionButton);
|
||||
Drawable icon = ContextCompat.getDrawable(mContext, R.drawable.ic_my_location);
|
||||
Drawable icon = ContextCompat.getDrawable(mContext, R.drawable.ic_location_crosshair);
|
||||
int colorAccent = ContextCompat.getColor(mContext,
|
||||
UiUtils.getStyledResourceId(mContext, androidx.appcompat.R.attr.colorAccent));
|
||||
mActionIcon.setImageDrawable(Graphics.tint(icon, colorAccent));
|
||||
|
@ -485,5 +487,7 @@ final class RoutingBottomMenuController implements View.OnClickListener
|
|||
int pointType = (Integer) mActionMessage.getTag();
|
||||
mListener.onSearchRoutePoint(pointType);
|
||||
}
|
||||
else if (id == R.id.btn__manage_route && mListener != null)
|
||||
mListener.onManageRouteOpen();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,4 +5,5 @@ public interface RoutingBottomMenuListener
|
|||
void onUseMyPositionAsStart();
|
||||
void onSearchRoutePoint(@RoutePointInfo.RouteMarkType int type);
|
||||
void onRoutingStart();
|
||||
void onManageRouteOpen();
|
||||
}
|
||||
|
|
|
@ -60,7 +60,7 @@ public class RoutingController
|
|||
default void onResetToPlanningState() {}
|
||||
default void onBuiltRoute() {}
|
||||
default void onDrivingOptionsWarning() {}
|
||||
default boolean isSubwayEnabled() { return false; }
|
||||
|
||||
default void onCommonBuildError(int lastResultCode, @NonNull String[] lastMissingMaps) {}
|
||||
default void onDrivingOptionsBuildError() {}
|
||||
|
||||
|
@ -333,45 +333,23 @@ public class RoutingController
|
|||
{
|
||||
setState(State.NONE);
|
||||
setBuildState(BuildState.NONE);
|
||||
prepare(getStartPoint(), getEndPoint(), false);
|
||||
prepare(getStartPoint(), getEndPoint());
|
||||
}
|
||||
|
||||
public void prepare(@Nullable MapObject startPoint, @Nullable MapObject endPoint)
|
||||
{
|
||||
prepare(startPoint, endPoint, false);
|
||||
}
|
||||
|
||||
public void prepare(@Nullable MapObject startPoint, @Nullable MapObject endPoint, boolean fromApi)
|
||||
{
|
||||
Logger.d(TAG, "prepare (" + (endPoint == null ? "route)" : "p2p)"));
|
||||
initLastRouteType(startPoint, endPoint, fromApi);
|
||||
initLastRouteType(startPoint, endPoint);
|
||||
prepare(startPoint, endPoint, mLastRouterType);
|
||||
}
|
||||
|
||||
private void initLastRouteType(@Nullable MapObject startPoint, @Nullable MapObject endPoint,
|
||||
boolean fromApi)
|
||||
private void initLastRouteType(@Nullable MapObject startPoint, @Nullable MapObject endPoint)
|
||||
{
|
||||
if (shouldForceTransitRoute(fromApi))
|
||||
{
|
||||
mLastRouterType = Framework.ROUTER_TYPE_TRANSIT;
|
||||
return;
|
||||
}
|
||||
|
||||
if (startPoint != null && endPoint != null)
|
||||
mLastRouterType = Framework.nativeGetBestRouter(startPoint.getLat(), startPoint.getLon(),
|
||||
endPoint.getLat(), endPoint.getLon());
|
||||
}
|
||||
|
||||
private boolean isSubwayEnabled()
|
||||
{
|
||||
return mContainer != null && mContainer.isSubwayEnabled();
|
||||
}
|
||||
|
||||
private boolean shouldForceTransitRoute(boolean fromApi)
|
||||
{
|
||||
return mState == State.NONE && isSubwayEnabled() && !fromApi;
|
||||
}
|
||||
|
||||
public void prepare(final @Nullable MapObject startPoint, final @Nullable MapObject endPoint,
|
||||
@Framework.RouterType int routerType)
|
||||
{
|
||||
|
@ -426,6 +404,17 @@ public class RoutingController
|
|||
resetToPlanningStateIfNavigating();
|
||||
}
|
||||
|
||||
public void launchPlanning()
|
||||
{
|
||||
build();
|
||||
setState(State.PREPARE);
|
||||
startPlanning();
|
||||
if (mContainer != null)
|
||||
mContainer.updateMenu();
|
||||
if (mContainer != null)
|
||||
mContainer.onResetToPlanningState();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return False if not navigating, true otherwise
|
||||
*/
|
||||
|
|
|
@ -2,10 +2,13 @@ package app.organicmaps.routing;
|
|||
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.graphics.Typeface;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Pair;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
@ -35,7 +38,15 @@ public class RoutingErrorDialogFragment extends BaseRoutingErrorDialogFragment
|
|||
ResultCodesHelper.getDialogTitleSubtitle(requireContext(), mResultCode, mMissingMaps.size());
|
||||
Pair<String, String> titleMessage = resHolder.getTitleMessage();
|
||||
|
||||
builder.setTitle(titleMessage.first);
|
||||
TextView titleView = new TextView(requireContext());
|
||||
titleView.setText(titleMessage.first);
|
||||
titleView.setPadding(65, 32, 32, 16);
|
||||
titleView.setTextSize(18);
|
||||
titleView.setMaxLines(4);
|
||||
titleView.setEllipsize(TextUtils.TruncateAt.END);
|
||||
titleView.setTypeface(null, Typeface.BOLD);
|
||||
builder.setCustomTitle(titleView);
|
||||
|
||||
mMessage = titleMessage.second;
|
||||
builder.setNegativeButton(resHolder.getCancelBtnResId(), null);
|
||||
if (ResultCodesHelper.isDownloadable(mResultCode, mMissingMaps.size()))
|
||||
|
|
|
@ -42,8 +42,7 @@ public class RoutingMapsDownloadFragment extends BaseRoutingErrorDialogFragment
|
|||
mMapsArray[i] = item.id;
|
||||
}
|
||||
|
||||
for (String map : mMaps)
|
||||
MapManager.nativeDownload(map);
|
||||
MapManager.startDownload(mMapsArray);
|
||||
}
|
||||
|
||||
private View setupFrame(View frame)
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
package app.organicmaps.routing;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.RadioGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.IdRes;
|
||||
import androidx.annotation.NonNull;
|
||||
|
@ -78,6 +80,7 @@ public class RoutingPlanController extends ToolbarController
|
|||
}
|
||||
|
||||
RoutingPlanController(View root, Activity activity,
|
||||
ActivityResultLauncher<Intent> startDrivingOptionsForResult,
|
||||
@NonNull RoutingPlanInplaceController.RoutingPlanListener routingPlanListener,
|
||||
@Nullable RoutingBottomMenuListener listener)
|
||||
{
|
||||
|
@ -103,7 +106,7 @@ public class RoutingPlanController extends ToolbarController
|
|||
View btn = mDrivingOptionsBtnContainer.findViewById(R.id.driving_options_btn);
|
||||
mDrivingOptionsImage = mFrame.findViewById(R.id.driving_options_btn_img);
|
||||
|
||||
btn.setOnClickListener(v -> DrivingOptionsActivity.start(requireActivity()));
|
||||
btn.setOnClickListener(v -> DrivingOptionsActivity.start(requireActivity(), startDrivingOptionsForResult));
|
||||
mDriverOptionsLayoutListener = new SelfTerminatedDrivingOptionsLayoutListener();
|
||||
mAnimToggle = MwmApplication.from(activity.getApplicationContext())
|
||||
.getResources().getInteger(R.integer.anim_default);
|
||||
|
|
|
@ -7,9 +7,9 @@ import android.view.ViewGroup;
|
|||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
|
||||
import app.organicmaps.Framework;
|
||||
import app.organicmaps.MwmActivity;
|
||||
import app.organicmaps.R;
|
||||
import app.organicmaps.base.BaseMwmFragment;
|
||||
|
||||
|
@ -21,15 +21,9 @@ public class RoutingPlanFragment extends BaseMwmFragment
|
|||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState)
|
||||
{
|
||||
final FragmentActivity activity = requireActivity();
|
||||
final MwmActivity activity = (MwmActivity) requireActivity();
|
||||
View res = inflater.inflate(R.layout.fragment_routing, container, false);
|
||||
RoutingBottomMenuListener listener = null;
|
||||
if (activity instanceof RoutingBottomMenuListener)
|
||||
listener = (RoutingBottomMenuListener) activity;
|
||||
|
||||
RoutingPlanInplaceController.RoutingPlanListener planListener =
|
||||
(RoutingPlanInplaceController.RoutingPlanListener) activity;
|
||||
mPlanController = new RoutingPlanController(res, activity, planListener, listener);
|
||||
mPlanController = new RoutingPlanController(res, activity, activity.startDrivingOptionsForResult, activity, activity);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,8 +2,10 @@ package app.organicmaps.routing;
|
|||
|
||||
import android.animation.Animator;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
|
@ -20,10 +22,11 @@ public class RoutingPlanInplaceController extends RoutingPlanController
|
|||
private Animator mAnimator;
|
||||
|
||||
public RoutingPlanInplaceController(@NonNull MwmActivity activity,
|
||||
ActivityResultLauncher<Intent> startDrivingOptionsForResult,
|
||||
@NonNull RoutingPlanListener routingPlanListener,
|
||||
@NonNull RoutingBottomMenuListener listener)
|
||||
{
|
||||
super(activity.findViewById(R.id.routing_plan_frame), activity, routingPlanListener, listener);
|
||||
super(activity.findViewById(R.id.routing_plan_frame), activity, startDrivingOptionsForResult, routingPlanListener, listener);
|
||||
mRoutingPlanListener = routingPlanListener;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package app.organicmaps.search;
|
||||
package app.organicmaps.sdk.search;
|
||||
|
||||
import androidx.annotation.Keep;
|
||||
import androidx.annotation.Nullable;
|
||||
|
@ -6,11 +6,11 @@ import androidx.annotation.Nullable;
|
|||
/**
|
||||
* Native search will return results via this interface.
|
||||
*/
|
||||
public interface NativeBookmarkSearchListener
|
||||
public interface BookmarkSearchListener
|
||||
{
|
||||
/**
|
||||
* @param bookmarkIds Founded bookmark ids.
|
||||
* @param timestamp Timestamp of search request.
|
||||
* @param timestamp Timestamp of search request.
|
||||
*/
|
||||
// Used by JNI.
|
||||
@Keep
|
||||
|
@ -19,7 +19,7 @@ public interface NativeBookmarkSearchListener
|
|||
|
||||
/**
|
||||
* @param bookmarkIds Founded bookmark ids.
|
||||
* @param timestamp Timestamp of search request.
|
||||
* @param timestamp Timestamp of search request.
|
||||
*/
|
||||
// Used by JNI.
|
||||
@Keep
|
|
@ -1,8 +1,8 @@
|
|||
package app.organicmaps.search;
|
||||
package app.organicmaps.sdk.search;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
class DisplayedCategories
|
||||
public class DisplayedCategories
|
||||
{
|
||||
@NonNull
|
||||
public static String[] getKeys()
|
|
@ -0,0 +1,19 @@
|
|||
package app.organicmaps.sdk.search;
|
||||
|
||||
import androidx.annotation.Keep;
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
public interface MapSearchListener
|
||||
{
|
||||
// Called from JNI.
|
||||
@Keep
|
||||
@SuppressWarnings("unused")
|
||||
record Result(String countryId, String matchedString)
|
||||
{
|
||||
}
|
||||
|
||||
// Called from JNI.
|
||||
@Keep
|
||||
@SuppressWarnings("unused")
|
||||
void onMapSearchResults(@NonNull Result[] results, long timestamp, boolean isLast);
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package app.organicmaps.search;
|
||||
package app.organicmaps.sdk.search;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
@ -38,12 +38,12 @@ public class Popularity implements Parcelable
|
|||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags)
|
||||
public void writeToParcel(@NonNull Parcel dest, int flags)
|
||||
{
|
||||
dest.writeInt(this.mType.ordinal());
|
||||
}
|
||||
|
||||
protected Popularity(Parcel in)
|
||||
protected Popularity(@NonNull Parcel in)
|
||||
{
|
||||
int tmpMPopularity = in.readInt();
|
||||
this.mType = Type.values()[tmpMPopularity];
|
|
@ -1,4 +1,4 @@
|
|||
package app.organicmaps.search;
|
||||
package app.organicmaps.sdk.search;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
|
@ -14,9 +14,9 @@ import app.organicmaps.util.concurrency.UiThread;
|
|||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
public enum SearchEngine implements NativeSearchListener,
|
||||
NativeMapSearchListener,
|
||||
NativeBookmarkSearchListener
|
||||
public enum SearchEngine implements SearchListener,
|
||||
MapSearchListener,
|
||||
BookmarkSearchListener
|
||||
{
|
||||
INSTANCE;
|
||||
|
||||
|
@ -30,7 +30,7 @@ public enum SearchEngine implements NativeSearchListener,
|
|||
UiThread.run(
|
||||
() ->
|
||||
{
|
||||
for (NativeSearchListener listener : mListeners)
|
||||
for (SearchListener listener : mListeners)
|
||||
listener.onResultsUpdate(results, timestamp);
|
||||
});
|
||||
}
|
||||
|
@ -41,18 +41,18 @@ public enum SearchEngine implements NativeSearchListener,
|
|||
UiThread.run(
|
||||
() ->
|
||||
{
|
||||
for (NativeSearchListener listener : mListeners)
|
||||
for (SearchListener listener : mListeners)
|
||||
listener.onResultsEnd(timestamp);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMapSearchResults(final NativeMapSearchListener.Result[] results, final long timestamp, final boolean isLast)
|
||||
public void onMapSearchResults(@NonNull final MapSearchListener.Result[] results, final long timestamp, final boolean isLast)
|
||||
{
|
||||
UiThread.run(
|
||||
() ->
|
||||
{
|
||||
for (NativeMapSearchListener listener : mMapListeners)
|
||||
for (MapSearchListener listener : mMapListeners)
|
||||
listener.onMapSearchResults(results, timestamp, isLast);
|
||||
});
|
||||
}
|
||||
|
@ -60,55 +60,53 @@ public enum SearchEngine implements NativeSearchListener,
|
|||
@Override
|
||||
public void onBookmarkSearchResultsUpdate(@Nullable long[] bookmarkIds, long timestamp)
|
||||
{
|
||||
for (NativeBookmarkSearchListener listener : mBookmarkListeners)
|
||||
for (BookmarkSearchListener listener : mBookmarkListeners)
|
||||
listener.onBookmarkSearchResultsUpdate(bookmarkIds, timestamp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBookmarkSearchResultsEnd(@Nullable long[] bookmarkIds, long timestamp)
|
||||
{
|
||||
for (NativeBookmarkSearchListener listener : mBookmarkListeners)
|
||||
for (BookmarkSearchListener listener : mBookmarkListeners)
|
||||
listener.onBookmarkSearchResultsEnd(bookmarkIds, timestamp);
|
||||
}
|
||||
|
||||
private final ObserverList<NativeSearchListener> mListeners = new ObserverList<>();
|
||||
private final ObserverList<SearchListener> mListeners = new ObserverList<>();
|
||||
|
||||
private final ObserverList<NativeMapSearchListener> mMapListeners = new ObserverList<>();
|
||||
private final ObserverList<MapSearchListener> mMapListeners = new ObserverList<>();
|
||||
|
||||
private final ObserverList<NativeBookmarkSearchListener> mBookmarkListeners = new ObserverList<>();
|
||||
private final ObserverList<BookmarkSearchListener> mBookmarkListeners = new ObserverList<>();
|
||||
|
||||
public void addListener(NativeSearchListener listener)
|
||||
public void addListener(SearchListener listener)
|
||||
{
|
||||
mListeners.addObserver(listener);
|
||||
}
|
||||
|
||||
public void removeListener(NativeSearchListener listener)
|
||||
public void removeListener(SearchListener listener)
|
||||
{
|
||||
mListeners.removeObserver(listener);
|
||||
}
|
||||
|
||||
public void addMapListener(NativeMapSearchListener listener)
|
||||
public void addMapListener(MapSearchListener listener)
|
||||
{
|
||||
mMapListeners.addObserver(listener);
|
||||
}
|
||||
|
||||
public void removeMapListener(NativeMapSearchListener listener)
|
||||
public void removeMapListener(MapSearchListener listener)
|
||||
{
|
||||
mMapListeners.removeObserver(listener);
|
||||
}
|
||||
|
||||
public void addBookmarkListener(NativeBookmarkSearchListener listener)
|
||||
public void addBookmarkListener(BookmarkSearchListener listener)
|
||||
{
|
||||
mBookmarkListeners.addObserver(listener);
|
||||
}
|
||||
|
||||
public void removeBookmarkListener(NativeBookmarkSearchListener listener)
|
||||
public void removeBookmarkListener(BookmarkSearchListener listener)
|
||||
{
|
||||
mBookmarkListeners.removeObserver(listener);
|
||||
}
|
||||
|
||||
private native void nativeInit();
|
||||
|
||||
/**
|
||||
*
|
||||
* @param context
|
||||
|
@ -116,7 +114,7 @@ public enum SearchEngine implements NativeSearchListener,
|
|||
* @return whether search was actually started.
|
||||
*/
|
||||
@MainThread
|
||||
public boolean search(@NonNull Context context, String query, boolean isCategory,
|
||||
public boolean search(@NonNull Context context, @NonNull String query, boolean isCategory,
|
||||
long timestamp, boolean hasLocation, double lat, double lon)
|
||||
{
|
||||
return nativeRunSearch(query.getBytes(StandardCharsets.UTF_8), isCategory,
|
||||
|
@ -146,7 +144,7 @@ public enum SearchEngine implements NativeSearchListener,
|
|||
}
|
||||
|
||||
@MainThread
|
||||
public static void searchMaps(@NonNull Context context, String query, long timestamp)
|
||||
public static void searchMaps(@NonNull Context context, @NonNull String query, long timestamp)
|
||||
{
|
||||
nativeRunSearchMaps(query.getBytes(StandardCharsets.UTF_8), Language.getKeyboardLocale(context),
|
||||
timestamp);
|
||||
|
@ -208,6 +206,8 @@ public enum SearchEngine implements NativeSearchListener,
|
|||
nativeInit();
|
||||
}
|
||||
|
||||
private native void nativeInit();
|
||||
|
||||
/**
|
||||
* @param bytes utf-8 formatted bytes of query.
|
||||
*/
|
|
@ -1,4 +1,4 @@
|
|||
package app.organicmaps.search;
|
||||
package app.organicmaps.sdk.search;
|
||||
|
||||
import androidx.annotation.Keep;
|
||||
import androidx.annotation.NonNull;
|
||||
|
@ -6,10 +6,10 @@ import androidx.annotation.NonNull;
|
|||
/**
|
||||
* Native search will return results via this interface.
|
||||
*/
|
||||
public interface NativeSearchListener
|
||||
public interface SearchListener
|
||||
{
|
||||
/**
|
||||
* @param results Search results.
|
||||
* @param results Search results.
|
||||
* @param timestamp Timestamp of search request.
|
||||
*/
|
||||
// Called by JNI.
|
|
@ -1,8 +1,7 @@
|
|||
package app.organicmaps.search;
|
||||
package app.organicmaps.sdk.search;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Pair;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
|
@ -50,6 +49,8 @@ public final class SearchRecents
|
|||
}
|
||||
|
||||
private static native void nativeGetList(List<String> result);
|
||||
|
||||
private static native void nativeAdd(String locale, String query);
|
||||
|
||||
private static native void nativeClear();
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package app.organicmaps.search;
|
||||
package app.organicmaps.sdk.search;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Typeface;
|
||||
|
@ -32,7 +32,7 @@ public class SearchResult
|
|||
public static final int OPEN_NOW_NO = 2;
|
||||
|
||||
public static final SearchResult EMPTY = new SearchResult("", "", 0, 0,
|
||||
new int[] {}, new int[] {});
|
||||
new int[]{}, new int[]{});
|
||||
|
||||
// Used by JNI.
|
||||
@Keep
|
|
@ -18,6 +18,7 @@ import androidx.fragment.app.Fragment;
|
|||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import app.organicmaps.R;
|
||||
import app.organicmaps.sdk.search.DisplayedCategories;
|
||||
import app.organicmaps.util.ThemeUtils;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
|
@ -26,9 +27,10 @@ import java.lang.annotation.RetentionPolicy;
|
|||
class CategoriesAdapter extends RecyclerView.Adapter<CategoriesAdapter.ViewHolder>
|
||||
{
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef({ TYPE_CATEGORY })
|
||||
@interface ViewType {}
|
||||
private static final int TYPE_CATEGORY = 0;
|
||||
@IntDef({ ViewType.CATEGORY })
|
||||
@interface ViewType {
|
||||
int CATEGORY = 0;
|
||||
}
|
||||
|
||||
@StringRes
|
||||
private int[] mCategoryResIds;
|
||||
|
@ -121,15 +123,16 @@ class CategoriesAdapter extends RecyclerView.Adapter<CategoriesAdapter.ViewHolde
|
|||
@ViewType
|
||||
public int getItemViewType(int position)
|
||||
{
|
||||
return TYPE_CATEGORY;
|
||||
return ViewType.CATEGORY;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(ViewGroup parent, @ViewType int viewType)
|
||||
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, @ViewType int viewType)
|
||||
{
|
||||
View view;
|
||||
ViewHolder viewHolder;
|
||||
if (viewType == TYPE_CATEGORY)
|
||||
if (viewType == ViewType.CATEGORY)
|
||||
{
|
||||
view = mInflater.inflate(R.layout.item_search_category, parent, false);
|
||||
viewHolder = new ViewHolder(view, (TextView) view);
|
||||
|
|
|
@ -6,7 +6,8 @@ import androidx.annotation.NonNull;
|
|||
import androidx.annotation.Nullable;
|
||||
|
||||
import androidx.core.view.ViewCompat;
|
||||
import app.organicmaps.util.WindowInsetUtils;
|
||||
|
||||
import app.organicmaps.sdk.search.SearchEngine;
|
||||
import app.organicmaps.util.WindowInsetUtils.PaddingInsetsListener;
|
||||
import app.organicmaps.widget.SearchToolbarController;
|
||||
import app.organicmaps.util.UiUtils;
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
package app.organicmaps.search;
|
||||
|
||||
import androidx.annotation.Keep;
|
||||
|
||||
public interface NativeMapSearchListener
|
||||
{
|
||||
// Called from JNI.
|
||||
@Keep
|
||||
@SuppressWarnings("unused")
|
||||
class Result
|
||||
{
|
||||
public final String countryId;
|
||||
public final String matchedString;
|
||||
|
||||
public Result(String countryId, String matchedString)
|
||||
{
|
||||
this.countryId = countryId;
|
||||
this.matchedString = matchedString;
|
||||
}
|
||||
}
|
||||
|
||||
// Called from JNI.
|
||||
@Keep
|
||||
@SuppressWarnings("unused")
|
||||
void onMapSearchResults(Result[] results, long timestamp, boolean isLast);
|
||||
}
|
|
@ -2,10 +2,6 @@ package app.organicmaps.search;
|
|||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Typeface;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.Spanned;
|
||||
import android.text.style.StyleSpan;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
@ -17,6 +13,7 @@ import androidx.core.content.ContextCompat;
|
|||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import app.organicmaps.R;
|
||||
import app.organicmaps.sdk.search.SearchResult;
|
||||
import app.organicmaps.util.Graphics;
|
||||
import app.organicmaps.util.ThemeUtils;
|
||||
import app.organicmaps.util.UiUtils;
|
||||
|
|
|
@ -3,6 +3,7 @@ package app.organicmaps.search;
|
|||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.location.Location;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
|
@ -10,6 +11,8 @@ import android.view.LayoutInflater;
|
|||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.activity.result.contract.ActivityResultContracts;
|
||||
import androidx.annotation.CallSuper;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
@ -22,6 +25,7 @@ import androidx.recyclerview.widget.RecyclerView;
|
|||
import androidx.viewpager.widget.ViewPager;
|
||||
import app.organicmaps.Framework;
|
||||
import app.organicmaps.MwmActivity;
|
||||
import app.organicmaps.MwmApplication;
|
||||
import app.organicmaps.R;
|
||||
import app.organicmaps.base.BaseMwmFragment;
|
||||
import app.organicmaps.bookmarks.data.FeatureId;
|
||||
|
@ -31,6 +35,10 @@ import app.organicmaps.downloader.MapManager;
|
|||
import app.organicmaps.location.LocationHelper;
|
||||
import app.organicmaps.location.LocationListener;
|
||||
import app.organicmaps.routing.RoutingController;
|
||||
import app.organicmaps.sdk.search.SearchEngine;
|
||||
import app.organicmaps.sdk.search.SearchListener;
|
||||
import app.organicmaps.sdk.search.SearchRecents;
|
||||
import app.organicmaps.sdk.search.SearchResult;
|
||||
import app.organicmaps.util.Config;
|
||||
import app.organicmaps.util.SharedPropertiesUtils;
|
||||
import app.organicmaps.util.UiUtils;
|
||||
|
@ -47,7 +55,7 @@ import java.util.Arrays;
|
|||
import java.util.List;
|
||||
|
||||
public class SearchFragment extends BaseMwmFragment
|
||||
implements NativeSearchListener,
|
||||
implements SearchListener,
|
||||
CategoriesAdapter.CategoriesUiListener
|
||||
{
|
||||
private long mLastQueryTimestamp;
|
||||
|
@ -113,10 +121,9 @@ public class SearchFragment extends BaseMwmFragment
|
|||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation") // https://github.com/organicmaps/organicmaps/issues/3630
|
||||
protected void startVoiceRecognition(Intent intent, int code)
|
||||
protected void startVoiceRecognition(Intent intent)
|
||||
{
|
||||
startActivityForResult(intent, code);
|
||||
startVoiceRecognitionForResult.launch(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -168,6 +175,11 @@ public class SearchFragment extends BaseMwmFragment
|
|||
private String mInitialLocale;
|
||||
private boolean mInitialSearchOnMap = false;
|
||||
|
||||
private final ActivityResultLauncher<Intent> startVoiceRecognitionForResult = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), activityResult ->
|
||||
{
|
||||
mToolbarController.onVoiceRecognitionResult(activityResult);
|
||||
});
|
||||
|
||||
private final LocationListener mLocationListener = new LocationListener()
|
||||
{
|
||||
@Override
|
||||
|
@ -299,8 +311,10 @@ public class SearchFragment extends BaseMwmFragment
|
|||
|
||||
SearchEngine.INSTANCE.addListener(this);
|
||||
|
||||
SharedPreferences preferences = MwmApplication.prefs(requireContext());
|
||||
int lastSelectedTabPosition = preferences.getInt(Config.KEY_PREF_LAST_SEARCHED_TAB, 0);
|
||||
if (SearchRecents.getSize() == 0 && Config.isSearchHistoryEnabled())
|
||||
pager.setCurrentItem(TabAdapter.Tab.CATEGORIES.ordinal());
|
||||
pager.setCurrentItem(lastSelectedTabPosition);
|
||||
|
||||
tabAdapter.setTabSelectedListener(tab -> mToolbarController.deactivate());
|
||||
|
||||
|
@ -525,14 +539,6 @@ public class SearchFragment extends BaseMwmFragment
|
|||
mToolbarController.showProgress(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation") // https://github.com/organicmaps/organicmaps/issues/3630
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data)
|
||||
{
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
mToolbarController.onActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onBackPressed()
|
||||
{
|
||||
|
|
|
@ -11,6 +11,7 @@ import androidx.recyclerview.widget.RecyclerView;
|
|||
import app.organicmaps.R;
|
||||
import app.organicmaps.location.LocationHelper;
|
||||
import app.organicmaps.routing.RoutingController;
|
||||
import app.organicmaps.sdk.search.SearchRecents;
|
||||
import app.organicmaps.widget.SearchToolbarController;
|
||||
import app.organicmaps.util.Graphics;
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package app.organicmaps.search;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.util.SparseArray;
|
||||
import android.view.View;
|
||||
|
||||
|
@ -11,6 +12,8 @@ import androidx.fragment.app.FragmentPagerAdapter;
|
|||
import androidx.viewpager.widget.ViewPager;
|
||||
|
||||
import com.google.android.material.tabs.TabLayout;
|
||||
|
||||
import app.organicmaps.MwmApplication;
|
||||
import app.organicmaps.R;
|
||||
import app.organicmaps.util.Graphics;
|
||||
import app.organicmaps.util.Config;
|
||||
|
@ -91,6 +94,9 @@ class TabAdapter extends FragmentPagerAdapter
|
|||
@Override
|
||||
public void onTabSelected(TabLayout.Tab tab)
|
||||
{
|
||||
SharedPreferences.Editor editor = MwmApplication.prefs(mContext).edit();
|
||||
editor.putInt(Config.KEY_PREF_LAST_SEARCHED_TAB, tab.getPosition());
|
||||
editor.apply();
|
||||
super.onTabSelected(tab);
|
||||
Graphics.tint(mContext, tab.getIcon(), androidx.appcompat.R.attr.colorAccent);
|
||||
}
|
||||
|
@ -151,7 +157,9 @@ class TabAdapter extends FragmentPagerAdapter
|
|||
ViewPager.OnPageChangeListener listener = new PageChangedListener(tabs);
|
||||
mPager.addOnPageChangeListener(listener);
|
||||
tabs.setOnTabSelectedListener(new OnTabSelectedListenerForViewPager(mPager));
|
||||
listener.onPageSelected(0);
|
||||
SharedPreferences preferences = MwmApplication.prefs(mPager.getContext());
|
||||
int lastSelectedTabPosition = preferences.getInt(Config.KEY_PREF_LAST_SEARCHED_TAB, 0);
|
||||
listener.onPageSelected(lastSelectedTabPosition);
|
||||
}
|
||||
|
||||
void setTabSelectedListener(OnTabSelectedListener listener)
|
||||
|
|
|
@ -3,10 +3,9 @@ package app.organicmaps.settings;
|
|||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import app.organicmaps.MwmActivity;
|
||||
import app.organicmaps.base.BaseMwmFragmentActivity;
|
||||
|
||||
public class DrivingOptionsActivity extends BaseMwmFragmentActivity
|
||||
|
@ -17,9 +16,9 @@ public class DrivingOptionsActivity extends BaseMwmFragmentActivity
|
|||
return DrivingOptionsFragment.class;
|
||||
}
|
||||
|
||||
public static void start(@NonNull Activity activity)
|
||||
public static void start(@NonNull Activity activity, ActivityResultLauncher<Intent> startDrivingOptionsForResult)
|
||||
{
|
||||
Intent intent = new Intent(activity, DrivingOptionsActivity.class);
|
||||
activity.startActivityForResult(intent, MwmActivity.REQ_CODE_DRIVING_OPTIONS);
|
||||
startDrivingOptionsForResult.launch(intent);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ import app.organicmaps.util.SharedPropertiesUtils;
|
|||
import app.organicmaps.util.ThemeSwitcher;
|
||||
import app.organicmaps.util.Utils;
|
||||
import app.organicmaps.util.log.LogsManager;
|
||||
import app.organicmaps.search.SearchRecents;
|
||||
import app.organicmaps.sdk.search.SearchRecents;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
|
||||
import java.util.Locale;
|
||||
|
@ -231,6 +231,8 @@ public class SettingsPrefsFragment extends BaseXmlSettingsFragment implements La
|
|||
|
||||
if (!SharedPropertiesUtils.shouldShowEmulateBadStorageSetting(requireContext()))
|
||||
removePreference(getString(R.string.pref_settings_general), pref);
|
||||
else
|
||||
pref.setVisible(true);
|
||||
}
|
||||
|
||||
private void initAutoZoomPrefsCallbacks()
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package app.organicmaps.settings;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
|
@ -11,6 +12,8 @@ import android.text.style.ForegroundColorSpan;
|
|||
import android.view.View;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.activity.result.contract.ActivityResultContracts;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
@ -35,7 +38,6 @@ import java.util.Map;
|
|||
|
||||
public class VoiceInstructionsSettingsFragment extends BaseXmlSettingsFragment
|
||||
{
|
||||
private static final int REQUEST_INSTALL_DATA = 1;
|
||||
|
||||
@NonNull
|
||||
@SuppressWarnings("NotNullFieldNotInitialized")
|
||||
|
@ -67,6 +69,14 @@ public class VoiceInstructionsSettingsFragment extends BaseXmlSettingsFragment
|
|||
private LanguageData mCurrentLanguage;
|
||||
private String mSelectedLanguage;
|
||||
|
||||
private final ActivityResultLauncher<Intent> startInstallDataIntentForResult = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), activityResult -> {
|
||||
|
||||
if(activityResult.getResultCode() == Activity.RESULT_OK)
|
||||
{
|
||||
onInstallDataResult();
|
||||
}
|
||||
});
|
||||
|
||||
private final Preference.OnPreferenceChangeListener mEnabledListener = (preference, newValue) -> {
|
||||
final boolean set = (Boolean) newValue;
|
||||
if (!set)
|
||||
|
@ -107,8 +117,7 @@ public class VoiceInstructionsSettingsFragment extends BaseXmlSettingsFragment
|
|||
if (lang.downloaded)
|
||||
setLanguage(lang);
|
||||
else
|
||||
UiUtils.startActivityForResult(VoiceInstructionsSettingsFragment.this,
|
||||
new Intent(TextToSpeech.Engine.ACTION_INSTALL_TTS_DATA), REQUEST_INSTALL_DATA);
|
||||
UiUtils.startActivityForResult(startInstallDataIntentForResult, new Intent(TextToSpeech.Engine.ACTION_INSTALL_TTS_DATA));
|
||||
|
||||
return false;
|
||||
};
|
||||
|
@ -181,21 +190,13 @@ public class VoiceInstructionsSettingsFragment extends BaseXmlSettingsFragment
|
|||
updateTts();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation") // https://github.com/organicmaps/organicmaps/issues/3630
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data)
|
||||
private void onInstallDataResult()
|
||||
{
|
||||
// Do not check resultCode here as it is always RESULT_CANCELED
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
updateTts();
|
||||
|
||||
if (requestCode == REQUEST_INSTALL_DATA)
|
||||
{
|
||||
updateTts();
|
||||
|
||||
LanguageData lang = mLanguages.get(mSelectedLanguage);
|
||||
if (lang != null && lang.downloaded)
|
||||
setLanguage(lang);
|
||||
}
|
||||
LanguageData lang = mLanguages.get(mSelectedLanguage);
|
||||
if (lang != null && lang.downloaded)
|
||||
setLanguage(lang);
|
||||
}
|
||||
|
||||
private void enableListeners(boolean enable)
|
||||
|
|
|
@ -7,7 +7,6 @@ import android.os.Build;
|
|||
import androidx.annotation.NonNull;
|
||||
import androidx.preference.PreferenceManager;
|
||||
import app.organicmaps.BuildConfig;
|
||||
import app.organicmaps.MwmActivity;
|
||||
import app.organicmaps.MwmApplication;
|
||||
import app.organicmaps.R;
|
||||
|
||||
|
@ -36,6 +35,8 @@ public final class Config
|
|||
private static final String KEY_DONATE_URL = "DonateUrl";
|
||||
private static final String KEY_PREF_SEARCH_HISTORY = "SearchHistoryEnabled";
|
||||
|
||||
public static final String KEY_PREF_LAST_SEARCHED_TAB = "LastSearchTab";
|
||||
|
||||
/**
|
||||
* The total number of app launches.
|
||||
*/
|
||||
|
|
|
@ -118,13 +118,14 @@ public class LocationUtils
|
|||
return ContextCompat.checkSelfPermission(context, ACCESS_FINE_LOCATION) == PERMISSION_GRANTED;
|
||||
}
|
||||
|
||||
public static boolean checkCoarseLocationPermission(@NonNull Context context)
|
||||
/**
|
||||
* Checks if the app has location permissions granted.
|
||||
* Returns true if either:
|
||||
* Only ACCESS_COARSE_LOCATION is granted
|
||||
* Both ACCESS_COARSE_LOCATION and ACCESS_FINE_LOCATION are granted
|
||||
*/
|
||||
public static boolean checkLocationPermission(@NonNull Context context)
|
||||
{
|
||||
return ContextCompat.checkSelfPermission(context, ACCESS_COARSE_LOCATION) == PERMISSION_GRANTED;
|
||||
}
|
||||
|
||||
public static boolean checkLocationPermission(@NonNull Context context)
|
||||
{
|
||||
return checkFineLocationPermission(context) || checkCoarseLocationPermission(context);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import androidx.annotation.NonNull;
|
|||
import app.organicmaps.MwmApplication;
|
||||
import app.organicmaps.R;
|
||||
|
||||
import java.text.NumberFormat;
|
||||
import java.util.Locale;
|
||||
|
||||
public class StringUtils
|
||||
|
@ -24,11 +25,31 @@ public class StringUtils
|
|||
return String.format(Locale.getDefault(), pattern, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns correct string representation of % (percent number) for different locales
|
||||
* For example, that's how the result of would look like:
|
||||
* — formatPercent(0.2319) will return 23.19% (in US locale)
|
||||
* — formatPercent(0.2319) will return %23.19 (in Turkish locale)
|
||||
* — formatPercent(0.2319) will return 23,19% (in Latvian locale)
|
||||
* — formatPercent(1.23) will return 123%
|
||||
* — formatPercent(0.000145) will return 0.01%
|
||||
* — formatPercent(0.37) will return 37%
|
||||
*
|
||||
* @param fraction a double value, that represents a fraction of a whole
|
||||
* @return correct string representation of percent for different locales
|
||||
*/
|
||||
public static String formatPercent(double fraction) {
|
||||
NumberFormat percentFormat = NumberFormat.getPercentInstance();
|
||||
percentFormat.setMaximumFractionDigits(2);
|
||||
return percentFormat.format(fraction);
|
||||
}
|
||||
|
||||
public static native boolean nativeIsHtml(String text);
|
||||
|
||||
public static native boolean nativeContainsNormalized(String str, String substr);
|
||||
public static native String[] nativeFilterContainsNormalized(String[] strings, String substr);
|
||||
|
||||
public static native int nativeFormatSpeed(double metersPerSecond);
|
||||
public static native Pair<String, String> nativeFormatSpeedAndUnits(double metersPerSecond);
|
||||
public static native Distance nativeFormatDistance(double meters);
|
||||
@NonNull
|
||||
|
|
|
@ -9,6 +9,7 @@ import android.graphics.Color;
|
|||
import android.graphics.Rect;
|
||||
import android.os.Build;
|
||||
import android.text.TextUtils;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.TouchDelegate;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
@ -18,6 +19,8 @@ import android.view.WindowManager;
|
|||
import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.activity.result.ActivityResult;
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.annotation.AnyRes;
|
||||
import androidx.annotation.AttrRes;
|
||||
import androidx.annotation.ColorInt;
|
||||
|
@ -32,7 +35,6 @@ import androidx.core.graphics.Insets;
|
|||
import androidx.core.view.WindowCompat;
|
||||
import androidx.core.view.WindowInsetsCompat;
|
||||
import androidx.core.view.WindowInsetsControllerCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import app.organicmaps.MwmApplication;
|
||||
import app.organicmaps.R;
|
||||
|
@ -207,6 +209,15 @@ public final class UiUtils
|
|||
return context.getResources().getDimensionPixelSize(id);
|
||||
}
|
||||
|
||||
// this method returns the total height of the display (in pixels) including notch and other touchable areas
|
||||
public static int getDisplayTotalHeight(Context context)
|
||||
{
|
||||
DisplayMetrics metrics = new DisplayMetrics();
|
||||
WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
|
||||
windowManager.getDefaultDisplay().getRealMetrics(metrics);
|
||||
return metrics.heightPixels;
|
||||
}
|
||||
|
||||
public static void updateRedButton(Button button)
|
||||
{
|
||||
button.setTextColor(ThemeUtils.getColor(button.getContext(), button.isEnabled() ? R.attr.redButtonTextColor
|
||||
|
@ -362,10 +373,9 @@ public final class UiUtils
|
|||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation") // https://github.com/organicmaps/organicmaps/issues/3630
|
||||
public static void startActivityForResult(@NonNull Fragment fragment, @NonNull Intent intent, int requestCode)
|
||||
public static void startActivityForResult(ActivityResultLauncher<Intent> startForResult, @NonNull Intent intent)
|
||||
{
|
||||
fragment.startActivityForResult(intent, requestCode);
|
||||
startForResult.launch(intent);
|
||||
}
|
||||
|
||||
// utility class
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Reference in a new issue