Compare commits

..

1 Commits

Author SHA1 Message Date
Harry Bond
fe3df2e5c0 [fdroid] Release version 2025.08.13-8
Signed-off-by: Harry Bond <me@hbond.xyz>
2025-08-13 19:37:30 +01:00
4223 changed files with 81243 additions and 77419 deletions

View File

@@ -3,6 +3,7 @@ BasedOnStyle: Google
AccessModifierOffset: -2 AccessModifierOffset: -2
AlignAfterOpenBracket: Align AlignAfterOpenBracket: Align
AlignArrayOfStructures: Right
AlignConsecutiveMacros: AcrossEmptyLinesAndComments AlignConsecutiveMacros: AcrossEmptyLinesAndComments
AlignEscapedNewlines: LeftWithLastLine AlignEscapedNewlines: LeftWithLastLine
AlignOperands: AlignAfterOperator AlignOperands: AlignAfterOperator
@@ -39,10 +40,8 @@ ColumnLimit: 120
ConstructorInitializerIndentWidth: 2 ConstructorInitializerIndentWidth: 2
ContinuationIndentWidth: 4 ContinuationIndentWidth: 4
DerivePointerAlignment: false DerivePointerAlignment: false
EmptyLineBeforeAccessModifier: Always
IncludeBlocks: Preserve IncludeBlocks: Preserve
IndentAccessModifiers: false IndentAccessModifiers: false
IndentCaseLabels: false
IndentExternBlock: NoIndent IndentExternBlock: NoIndent
InsertBraces: false InsertBraces: false
InsertNewlineAtEOF: true InsertNewlineAtEOF: true
@@ -50,7 +49,6 @@ LambdaBodyIndentation: OuterScope
PackConstructorInitializers: CurrentLine PackConstructorInitializers: CurrentLine
PointerAlignment: Middle PointerAlignment: Middle
RemoveBracesLLVM: true RemoveBracesLLVM: true
RemoveSemicolon: true
QualifierAlignment: Right QualifierAlignment: Right
SpacesInContainerLiterals: false SpacesInContainerLiterals: false
Standard: Latest Standard: Latest

View File

@@ -1,8 +0,0 @@
# Files that should not be formatted.
./3party
# A patched copy of the https://registry.khronos.org/OpenGL/api/GLES3/gl3.h
./android/sdk/src/main/cpp/app/organicmaps/sdk/opengl/gl3stub.h
# Formatting it leads to crashes in runtime. Newer protobuf may fix it.
./libs/indexer/drules_struct.pb.cc
# No need to format this 3party tool.
tools/osmctools/*.c

View File

@@ -1,9 +1,8 @@
name: DCO name: dco
on: [pull_request] on: [pull_request]
jobs: jobs:
check: check:
runs-on: ubuntu-latest runs-on: codeberg-tiny
steps: steps:
- name: Check for Developer Certificate of Origin (DCO) compliance - uses: https://github.com/KineticCafe/actions-dco@v1
uses: https://github.com/KineticCafe/actions-dco@fb284c903a7673a3d4b0bdd104479a6f0d46dae7 # v1.3.6

View File

@@ -3,7 +3,7 @@ on:
workflow_dispatch: # Manual trigger workflow_dispatch: # Manual trigger
pull_request: pull_request:
paths: paths:
- qt/res/linux/app.comaps.comaps.desktop - qt/res/app.comaps.comaps.desktop
- .forgejo/workflows/desktop-file-check.yaml # Run check on self change - .forgejo/workflows/desktop-file-check.yaml # Run check on self change
jobs: jobs:
@@ -16,7 +16,7 @@ jobs:
with: with:
fetch-depth: 1 fetch-depth: 1
sparse-checkout: | sparse-checkout: |
qt/res/linux/app.comaps.comaps.desktop qt/res/app.comaps.comaps.desktop
- name: Install desktop-file-validate tool - name: Install desktop-file-validate tool
shell: bash shell: bash
@@ -27,4 +27,4 @@ jobs:
- name: Validate desktop file - name: Validate desktop file
shell: bash shell: bash
run: desktop-file-validate qt/res/linux/app.comaps.comaps.desktop && echo "Successfully validated .desktop file" run: desktop-file-validate qt/res/app.comaps.comaps.desktop && echo "Successfully validated .desktop file"

View File

@@ -133,14 +133,6 @@ jobs:
with: with:
key: ${{ github.workflow }}-unity-${{ matrix.compiler.CC }}-${{ matrix.CMAKE_BUILD_TYPE }} key: ${{ github.workflow }}-unity-${{ matrix.compiler.CC }}-${{ matrix.CMAKE_BUILD_TYPE }}
- name: Configure repository
shell: bash
env:
SKIP_MAP_DOWNLOAD: 1
SKIP_GENERATE_SYMBOLS: 1
SKIP_GENERATE_DRULES: 1
run: ./configure.sh
- name: CMake - name: CMake
shell: bash shell: bash
env: env:

View File

@@ -23,10 +23,6 @@ jobs:
shell: bash shell: bash
run: git submodule update --depth 1 --init --recursive --jobs=$(($(nproc) * 20)) run: git submodule update --depth 1 --init --recursive --jobs=$(($(nproc) * 20))
- name: Configure repository
shell: bash
run: SKIP_GENERATE_SYMBOLS=1 ./configure.sh
- name: Lint - name: Lint
shell: bash shell: bash
working-directory: android working-directory: android
@@ -66,7 +62,6 @@ jobs:
libxinerama-dev \ libxinerama-dev \
libxcursor-dev \ libxcursor-dev \
libxi-dev \ libxi-dev \
python3-protobuf \
zlib1g-dev zlib1g-dev
- name: Checkout sources - name: Checkout sources

43
.github/workflows/clang-format.yaml vendored Normal file
View File

@@ -0,0 +1,43 @@
name: clang-format
on:
push:
branches: [ master ]
paths:
- 'android/app/src/**.java'
- '.clang-format'
- '.github/workflows/clang-format.yml'
pull_request:
branches: [ master ]
paths:
- 'android/app/src/**.java'
- '.clang-format'
- '.github/workflows/clang-format.yml'
jobs:
check-formatting:
runs-on: ubuntu-latest
continue-on-error: true # TODO(AB): Remove this line when ready to enforce formatting.
steps:
- uses: actions/checkout@v4
- name: Install clang-format
run: |
sudo apt-get update
sudo apt-get install -y clang-format-19
clang-format-19 --version
- name: Check Java formatting
run: |
JAVA_FILES=($(find android/app/src -name '*.java'))
FORMATTING_ISSUES=$(clang-format-19 --dry-run --Werror $JAVA_FILES 2>&1 || true)
if [ -n "$FORMATTING_ISSUES" ]; then
echo "$FORMATTING_ISSUES"
echo ""
echo "To fix formatting, please run:"
echo " clang-format -i <file>"
exit 1
fi

View File

@@ -1,51 +0,0 @@
name: Code style check
on:
pull_request:
branches: [ master ]
paths: # Should stay in sync with tools/unix/clang-format.sh
- '.github/workflows/code-style-check.yaml'
- 'android/app/src/**.java'
- 'android/sdk/src/**.java'
- 'android/sdk/src/main/cpp/**.[ch]pp'
- 'dev_sandbox/**.[ch]pp'
- 'generator/**.[ch]pp'
- 'iphone/**.[ch]pp'
- 'iphone/**.[hm]'
- 'iphone/**.mm'
- 'libs/**.[ch]pp'
- 'libs/**.[hm]'
- '!libs/indexer/drules_struct.pb.h'
- 'libs/**.mm'
- 'qt/**.[ch]pp'
- 'qt/**.h'
- 'tools/**.[ch]pp'
- '.clang-format'
- '.clang-format-ignore'
jobs:
code-style-check:
runs-on: ubuntu-latest
steps:
- name: Install clang-format
run: |
sudo apt purge -y clang-format-18 # Remove default old version of clang-format
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
echo 'deb http://apt.llvm.org/noble/ llvm-toolchain-noble-20 main' | sudo tee /etc/apt/sources.list.d/llvm-toolchain-noble-20.list
sudo apt-get update
sudo apt-get install -y clang-format-20
sudo update-alternatives --force --install /usr/bin/clang-format clang-format /usr/bin/clang-format-20 10
sudo update-alternatives --force --install /usr/bin/git-clang-format git-clang-format /usr/bin/git-clang-format-20 10
clang-format --version
- name: Checkout sources
uses: actions/checkout@v4
- name: Check code style
run: tools/unix/clang-format.sh
- name: Post clang-format comments
if: failure()
uses: reviewdog/action-suggester@v1.21.0
with:
tool_name: clang-format
fail_level: error

View File

@@ -30,7 +30,6 @@ jobs:
run: | run: |
brew install qt \ brew install qt \
optipng optipng
pip3 install "protobuf<3.21" --break-system-packages
- name: Checkout sources - name: Checkout sources
uses: actions/checkout@v4 uses: actions/checkout@v4

20
.gitignore vendored
View File

@@ -14,10 +14,12 @@ stxxl.errlog
stxxl.log stxxl.log
screenlog.0 screenlog.0
data/symbols/*/design/
# symbols png/sdf are now generated at build # symbols png/sdf are now generated at build
data/symbols/**/symbols.png data/symbols/**/symbols.png
data/symbols/**/symbols.sdf data/symbols/**/symbols.sdf
data/colors_design.txt
data/patterns_design.txt
data/bookmarks data/bookmarks
data/edits.xml data/edits.xml
data/World.mwm data/World.mwm
@@ -25,15 +27,11 @@ data/WorldCoasts.mwm
data/world_mwm/* data/world_mwm/*
data/*_hash data/*_hash
data/drules_proto* data/drules_proto*
data/classificator.txt* data/classificator.txt
data/types.txt* data/types.txt
data/visibility.txt* data/visibility.txt
data/colors.txt* data/colors.txt
data/patterns.txt* data/patterns.txt
# TODO: designer is not used at the moment
# data/symbols/*/design/
# data/colors_design.txt
# data/patterns_design.txt
# Compiled Python # Compiled Python
*.pyc *.pyc
@@ -52,7 +50,7 @@ omim.sdf
*.suo *.suo
*.aps *.aps
*.rc *.rc
!qt/res/windows/windows.rc !qt/res/windows.rc
*.pdb *.pdb
out/ out/

4
.gitmodules vendored
View File

@@ -65,7 +65,3 @@
[submodule "3party/imgui/imgui"] [submodule "3party/imgui/imgui"]
path = 3party/imgui/imgui path = 3party/imgui/imgui
url = https://github.com/ocornut/imgui url = https://github.com/ocornut/imgui
[submodule "3party/glaze"]
path = 3party/glaze
url = https://github.com/stephenberry/glaze
branch = main

View File

@@ -57,7 +57,6 @@ endif()
add_subdirectory(agg) add_subdirectory(agg)
add_subdirectory(bsdiff-courgette) add_subdirectory(bsdiff-courgette)
add_subdirectory(glaze)
add_subdirectory(minizip) add_subdirectory(minizip)
add_subdirectory(open-location-code) add_subdirectory(open-location-code)
add_subdirectory(opening_hours) add_subdirectory(opening_hours)

View File

@@ -4,9 +4,7 @@ set(FT_DISABLE_HARFBUZZ ON)
add_subdirectory(freetype) add_subdirectory(freetype)
# Fix warning with ONE_PIXEL macro clash. # Fix warning with ONE_PIXEL macro clash.
if(NOT MSVC) target_compile_options(freetype PRIVATE -Wno-macro-redefined)
target_compile_options(freetype PRIVATE -Wno-macro-redefined)
endif()
# Use ft2build.h from the current directory instead of the default. # Use ft2build.h from the current directory instead of the default.
target_include_directories(freetype target_include_directories(freetype

Submodule 3party/glaze deleted from 5a58d7936e

View File

@@ -11,23 +11,14 @@ target_include_directories(${PROJECT_NAME}
harfbuzz/src harfbuzz/src
) )
if (MSVC) # Keep these settigns in sync with xcode/harfbuzz project.
target_compile_options(${PROJECT_NAME} target_compile_options(${PROJECT_NAME}
PRIVATE PRIVATE
/GR- -fno-rtti
/EHsc- -fno-exceptions
/Zc:threadSafeInit- -fno-threadsafe-statics
) $<$<OR:$<CXX_COMPILER_ID:Clang>,$<CXX_COMPILER_ID:AppleClang>>:-Wno-format-pedantic>
else() )
# Keep these settings in sync with xcode/harfbuzz project.
target_compile_options(${PROJECT_NAME}
PRIVATE
-fno-rtti
-fno-exceptions
-fno-threadsafe-statics
$<$<OR:$<CXX_COMPILER_ID:Clang>,$<CXX_COMPILER_ID:AppleClang>>:-Wno-format-pedantic>
)
endif()
target_compile_definitions(${PROJECT_NAME} target_compile_definitions(${PROJECT_NAME}
PRIVATE PRIVATE
@@ -37,20 +28,14 @@ target_compile_definitions(${PROJECT_NAME}
#$<$<BOOL:${APPLE}>:HAVE_CORETEXT> #$<$<BOOL:${APPLE}>:HAVE_CORETEXT>
HAVE_ATEXIT HAVE_ATEXIT
HAVE_GETPAGESIZE HAVE_GETPAGESIZE
HAVE_MMAP
HAVE_MPROTECT HAVE_MPROTECT
HAVE_PTHREAD
HAVE_SYSCONF HAVE_SYSCONF
HAVE_SYS_MMAN_H
HAVE_UNISTD_H
) )
if (NOT MSVC)
target_compile_definitions(${PROJECT_NAME}
PRIVATE
HAVE_MMAP
HAVE_PTHREAD
HAVE_SYS_MMAN_H
HAVE_UNISTD_H
)
endif()
target_link_libraries(${PROJECT_NAME} Freetype::Freetype) target_link_libraries(${PROJECT_NAME} Freetype::Freetype)
add_library(harfbuzz::harfbuzz ALIAS harfbuzz) add_library(harfbuzz::harfbuzz ALIAS harfbuzz)

View File

@@ -172,7 +172,6 @@ add_library(icuuc
icu/icu4c/source/common/uvector.cpp icu/icu4c/source/common/uvector.cpp
icu/icu4c/source/common/uvectr32.cpp icu/icu4c/source/common/uvectr32.cpp
icu/icu4c/source/common/uvectr64.h icu/icu4c/source/common/uvectr64.h
icu/icu4c/source/common/wintz.cpp
icu/icu4c/source/common/wintz.h icu/icu4c/source/common/wintz.h
) )

View File

@@ -11,7 +11,6 @@ set(SRC
protobuf/src/google/protobuf/message_lite.cc protobuf/src/google/protobuf/message_lite.cc
protobuf/src/google/protobuf/repeated_field.cc protobuf/src/google/protobuf/repeated_field.cc
protobuf/src/google/protobuf/stubs/atomicops_internals_x86_gcc.cc protobuf/src/google/protobuf/stubs/atomicops_internals_x86_gcc.cc
protobuf/src/google/protobuf/stubs/atomicops_internals_x86_msvc.cc
protobuf/src/google/protobuf/stubs/bytestream.cc protobuf/src/google/protobuf/stubs/bytestream.cc
protobuf/src/google/protobuf/stubs/common.cc protobuf/src/google/protobuf/stubs/common.cc
protobuf/src/google/protobuf/stubs/int128.cc protobuf/src/google/protobuf/stubs/int128.cc

View File

@@ -7,6 +7,7 @@
#include <boost/utility/enable_if.hpp> #include <boost/utility/enable_if.hpp>
#include <boost/utility.hpp> #include <boost/utility.hpp>
#include <boost/type_traits/is_pod.hpp> #include <boost/type_traits/is_pod.hpp>
#include <boost/iostreams/device/mapped_file.hpp>
#include "mappable_vector.hpp" #include "mappable_vector.hpp"
@@ -282,6 +283,12 @@ namespace succinct { namespace mapper {
return mapper.bytes_read(); return mapper.bytes_read();
} }
template <typename T>
size_t map(T& val, boost::iostreams::mapped_file_source const& m, uint64_t flags = 0, const char* friendly_name = "<TOP>")
{
return map(val, m.data(), flags, friendly_name);
}
template <typename T> template <typename T>
uint64_t size_of(T& val) uint64_t size_of(T& val)
{ {

View File

@@ -8,6 +8,7 @@
#include <stdint.h> #include <stdint.h>
#include <boost/iterator/iterator_facade.hpp> #include <boost/iterator/iterator_facade.hpp>
#include <boost/iostreams/device/mapped_file.hpp>
namespace succinct { namespace util { namespace succinct { namespace util {
@@ -204,6 +205,29 @@ namespace succinct { namespace util {
std::string m_cur_value; std::string m_cur_value;
}; };
struct mmap_lines
{
typedef buffer_line_iterator iterator;
typedef buffer_line_iterator const_iterator;
mmap_lines(std::string filename)
: m_map(filename)
{}
const_iterator begin() const
{
return const_iterator(m_map.data(), m_map.size());
}
const_iterator end() const
{
return const_iterator();
}
private:
boost::iostreams::mapped_file_source m_map;
};
struct input_error : std::invalid_argument struct input_error : std::invalid_argument
{ {
input_error(std::string const& what) input_error(std::string const& what)

View File

@@ -19,18 +19,7 @@ extern "C" {
#endif #endif
#include "vulkan_wrapper.h" #include "vulkan_wrapper.h"
#if defined(_WIN32)
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <windows.h>
#else
#include <dlfcn.h> #include <dlfcn.h>
#endif
int InitVulkan(void) { int InitVulkan(void) {
#if defined(__APPLE__) #if defined(__APPLE__)
@@ -41,9 +30,6 @@ int InitVulkan(void) {
if (!libvulkan) { if (!libvulkan) {
libvulkan = dlopen("libMoltenVK.dylib", RTLD_NOW | RTLD_LOCAL); libvulkan = dlopen("libMoltenVK.dylib", RTLD_NOW | RTLD_LOCAL);
} }
#elif defined( _WIN32 )
HMODULE libvulkan = LoadLibraryA("vulkan-1.dll");
auto dlsym = [](HMODULE h, char const * name) { return GetProcAddress(h, name); };
#else #else
void* libvulkan = dlopen("libvulkan.so.1", RTLD_NOW | RTLD_LOCAL); void* libvulkan = dlopen("libvulkan.so.1", RTLD_NOW | RTLD_LOCAL);
if (!libvulkan) { if (!libvulkan) {

View File

@@ -2,8 +2,8 @@ cmake_minimum_required(VERSION 3.22.1)
project(omim C CXX) project(omim C CXX)
set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD 20)
set(CMAKE_C_STANDARD 23) set(CMAKE_C_STANDARD 17)
# Our code does not rely on gnu extensions. # Our code does not rely on gnu extensions.
set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_C_EXTENSIONS OFF) set(CMAKE_C_EXTENSIONS OFF)
@@ -39,16 +39,18 @@ if (APPLE AND NOT ("${CMAKE_SYSTEM_NAME}" STREQUAL Android))
# https://gitlab.kitware.com/cmake/cmake/-/issues/21963 # https://gitlab.kitware.com/cmake/cmake/-/issues/21963
enable_language(OBJC) enable_language(OBJC)
set(CMAKE_OBJC_EXTENSIONS OFF) set(CMAKE_OBJC_EXTENSIONS OFF)
set(CMAKE_OBJC_STANDARD 23) set(CMAKE_OBJC_STANDARD 11)
set(CMAKE_OBJC_FLAGS -fobjc-arc) set(CMAKE_OBJC_FLAGS -fobjc-arc)
set(CMAKE_OBJC_VISIBILITY_PRESET hidden) set(CMAKE_OBJC_VISIBILITY_PRESET hidden)
enable_language(OBJCXX) enable_language(OBJCXX)
set(CMAKE_OBJCXX_EXTENSIONS OFF) set(CMAKE_OBJCXX_EXTENSIONS OFF)
set(CMAKE_OBJCXX_STANDARD 23) set(CMAKE_OBJCXX_STANDARD 20)
set(CMAKE_OBJCXX_FLAGS -fobjc-arc) set(CMAKE_OBJCXX_FLAGS -fobjc-arc)
set(CMAKE_OBJCXX_VISIBILITY_PRESET hidden) set(CMAKE_OBJCXX_VISIBILITY_PRESET hidden)
endif() endif()
execute_process(COMMAND "./configure.sh" WORKING_DIRECTORY ${OMIM_ROOT})
message(STATUS "Using compiler ${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION}") message(STATUS "Using compiler ${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION}")
if (CMAKE_UNITY_BUILD) if (CMAKE_UNITY_BUILD)
@@ -85,7 +87,7 @@ if (MSVC)
add_compile_options(/utf-8) add_compile_options(/utf-8)
add_link_options(/INCREMENTAL:NO) add_link_options(/INCREMENTAL:NO)
else() else()
add_compile_options(-ffast-math $<$<CXX_COMPILER_ID:GNU>:-Wno-psabi>) add_compile_options(-ffast-math)
endif() endif()
if (PLATFORM_WIN) if (PLATFORM_WIN)
@@ -183,7 +185,7 @@ if (NOT PLATFORM_IPHONE AND NOT PLATFORM_ANDROID)
endif() endif()
# To allow #include "base/file_name.hpp" in all sources. # To allow #include "base/file_name.hpp" in all sources.
include_directories("${CMAKE_HOME_DIRECTORY}" "${CMAKE_HOME_DIRECTORY}/libs" "${CMAKE_HOME_DIRECTORY}/tools") include_directories(${CMAKE_HOME_DIRECTORY})
if (USE_PCH) if (USE_PCH)
message(STATUS "Precompiled headers are ON") message(STATUS "Precompiled headers are ON")
@@ -195,7 +197,7 @@ if (USE_PCH)
endif() endif()
# Should be on the root level, not in 3party, so tests can get these dependencies. # Should be on the root level, not in 3party, so tests can get these dependencies.
if (PLATFORM_LINUX) if (PLATFORM_LINUX OR PLATFORM_WIN)
find_package(ICU COMPONENTS uc i18n data REQUIRED) find_package(ICU COMPONENTS uc i18n data REQUIRED)
find_package(Freetype REQUIRED) find_package(Freetype REQUIRED)
find_package(harfbuzz REQUIRED) find_package(harfbuzz REQUIRED)
@@ -225,22 +227,49 @@ endif()
# Used in qt/ and shaders/ # Used in qt/ and shaders/
find_package(Python3 REQUIRED COMPONENTS Interpreter) find_package(Python3 REQUIRED COMPONENTS Interpreter)
add_subdirectory(libs) add_subdirectory(base)
add_subdirectory(coding)
add_subdirectory(descriptions)
add_subdirectory(drape)
add_subdirectory(drape_frontend)
add_subdirectory(editor)
add_subdirectory(ge0)
add_subdirectory(generator/mwm_diff)
add_subdirectory(geometry)
add_subdirectory(indexer)
add_subdirectory(kml)
add_subdirectory(map)
add_subdirectory(cppjansson)
add_subdirectory(platform)
add_subdirectory(routing)
add_subdirectory(routing_common)
add_subdirectory(search)
add_subdirectory(shaders)
add_subdirectory(storage)
add_subdirectory(tracking)
add_subdirectory(traffic)
add_subdirectory(transit)
if (PLATFORM_DESKTOP) if (PLATFORM_DESKTOP)
add_subdirectory(dev_sandbox) omim_add_tool_subdirectory(feature_list)
add_subdirectory(generator) add_subdirectory(generator)
add_subdirectory(tools) add_subdirectory(openlr)
add_subdirectory(poly_borders)
omim_add_tool_subdirectory(topography_generator)
add_subdirectory(track_analyzing)
omim_add_tool_subdirectory(track_generator)
if (NOT SKIP_QT_GUI) if (NOT SKIP_QT_GUI)
add_subdirectory(qt) add_subdirectory(qt)
omim_add_tool_subdirectory(skin_generator)
endif() endif()
if (GENERATOR_TOOL) if (GENERATOR_TOOL)
add_compile_options(-march=native -mtune=native) add_compile_options(-march=native -mtune=native)
message(STATUS "target CPU optimizations enabled, produced binaries will NOT work on a different CPU") message(STATUS "target CPU optimizations enabled, produced binaries will NOT work on a different CPU")
endif() endif()
add_subdirectory(dev_sandbox)
endif() endif()
omim_add_test_subdirectory(libs/qt_tstfrm) omim_add_test_subdirectory(qt_tstfrm)
if (PLATFORM_ANDROID) if (PLATFORM_ANDROID)
add_subdirectory(android/sdk/src/main/cpp) add_subdirectory(android/sdk/src/main/cpp)

View File

@@ -19,20 +19,20 @@
</div> </div>
<div align="center"> <div align="center">
<p align="center"> <p align="center">
<a href="https://codeberg.org/comaps/comaps/src/branch/main/LICENSE"> <a href="https://codeberg.org/comaps/comaps/releases">
<img src="https://img.shields.io/github/license/comaps/comaps?style=for-the-badge&logo=opensourceinitiative&logoColor=white&color=588157" alt="License"/> <img src="https://img.shields.io/github/license/comaps/comaps?style=for-the-badge&logo=opensourceinitiative&logoColor=white&color=588157" alt="License" style="width: 90%; max-width: 150px;"/>
</a> </a>
<a href="https://github.com/comaps/comaps/actions/workflows/android-check.yaml"> <a href="https://github.com/comaps/comaps/actions/workflows/android-check.yaml">
<img src="https://img.shields.io/github/actions/workflow/status/comaps/comaps/.github/workflows/android-check.yaml?label=Android%20Build&logo=android&logoColor=white&style=for-the-badge&color=588157" alt="Android Build Status"/> <img src="https://img.shields.io/github/actions/workflow/status/comaps/comaps/.github/workflows/android-check.yaml?label=Android%20Build&logo=android&logoColor=white&style=for-the-badge&color=588157" alt="Android Build Status" style="width: 90%; max-width: 170px;"/>
</a> </a>
<a href="https://github.com/comaps/comaps/actions/workflows/ios-check.yaml"> <a href="https://github.com/comaps/comaps/actions/workflows/ios-check.yaml">
<img src="https://img.shields.io/github/actions/workflow/status/comaps/comaps/.github/workflows/ios-check.yaml?label=iOS%20Build&logo=apple&logoColor=white&style=for-the-badge&color=588157" alt="iOS Build Status"/> <img src="https://img.shields.io/github/actions/workflow/status/comaps/comaps/.github/workflows/ios-check.yaml?label=iOS%20Build&logo=apple&logoColor=white&style=for-the-badge&color=588157" alt="iOS Build Status" style="width: 90%; max-width: 145px;"/>
</a> </a>
<a href="https://opencollective.com/comaps"> <a href="https://opencollective.com/comaps">
<img src="https://img.shields.io/opencollective/all/comaps?label=Open%20Collective%20Donors&logo=opencollective&logoColor=white&style=for-the-badge&color=588157" alt="Open Collective Donors"/> <img src="https://img.shields.io/opencollective/all/comaps?label=Open%20Collective%20Donors&logo=opencollective&logoColor=white&style=for-the-badge&color=588157" alt="Open Collective Donors" style="width: 90%; max-width: 191px;"/>
</a> </a>
<a href="https://liberapay.com/CoMaps"> <a href="https://liberapay.com/CoMaps">
<img src="https://img.shields.io/liberapay/patrons/CoMaps.svg?label=Liberapay%20Patrons&logo=liberapay&logoColor=white&style=for-the-badge&color=588157" alt="Liberapay Patrons"/> <img src="https://img.shields.io/liberapay/patrons/CoMaps.svg?label=Liberapay%20Patrons&logo=liberapay&logoColor=white&style=for-the-badge&color=588157" alt="Liberapay Patrons" style="width: 90%; max-width: 160px;"/>
</a> </a>
</p> </p>
</div> </div>
@@ -41,6 +41,8 @@
A community-led free & open source maps app based on [OpenStreetMap](https://www.openstreetmap.org), built for transparency, privacy, and not-for-profit values. A fork of Organic Maps, originally based on Maps.ME. A community-led free & open source maps app based on [OpenStreetMap](https://www.openstreetmap.org), built for transparency, privacy, and not-for-profit values. A fork of Organic Maps, originally based on Maps.ME.
**Available for:** Android, iOS, ARM macOS, and alpha Linux/macOS desktop builds (also usable on Linux phones).
<p align="center"> <p align="center">
<a href="https://apps.apple.com/app/comaps/id6747180809"> <a href="https://apps.apple.com/app/comaps/id6747180809">
<img src="docs/badges/apple-appstore.png" alt="App Store" width="160"/> <img src="docs/badges/apple-appstore.png" alt="App Store" width="160"/>
@@ -75,10 +77,9 @@ A community-led free & open source maps app based on [OpenStreetMap](https://www
## ⚡️ Highlights ## ⚡️ Highlights
- **Offline-first**: Navigate without a connection - **Offline-first**: Navigate without a connection
- **Privacy-respecting**: No tracking, ads or data collection - **Privacy-respecting**: No tracking, Ads or data collection
- **Lightweight**: Battery- and space-efficient - **Lightweight**: Battery- and space-efficient
- **Simple**: Polished, user-focused interface - **Simple**: Polished, user-focused interface
- **Cross-platform**: Available for Android, iOS, MacOS, and Linux.
- **Community-built**: Free, open source, and collaborative - **Community-built**: Free, open source, and collaborative
- **Transparent**: Open finances and governance - **Transparent**: Open finances and governance
@@ -139,7 +140,7 @@ There is a dedicated [Zulip](https://codeberg.org/comaps/Governance/src/branch/m
## 💸 Funding ## 💸 Funding
CoMaps is free. To fund development, we rely on your voluntary support ♥️ CoMaps is free. To stay that way, it relies on your support.
Donate via [OpenCollective](https://opencollective.com/comaps/donate) or [Liberapay](https://liberapay.com/CoMaps). Donate via [OpenCollective](https://opencollective.com/comaps/donate) or [Liberapay](https://liberapay.com/CoMaps).
The project's financial information is completely open and transparent at [our Open Collective](https://opencollective.com/comaps). The project's financial information is completely open and transparent at [our Open Collective](https://opencollective.com/comaps).

View File

@@ -138,6 +138,8 @@ android {
disable 'MissingTranslation' disable 'MissingTranslation'
// https://github.com/organicmaps/organicmaps/issues/3551 // https://github.com/organicmaps/organicmaps/issues/3551
disable 'MissingQuantity', 'UnusedQuantity' disable 'MissingQuantity', 'UnusedQuantity'
// https://github.com/organicmaps/organicmaps/issues/3550
disable 'ByteOrderMark'
// https://github.com/organicmaps/organicmaps/issues/1077 // https://github.com/organicmaps/organicmaps/issues/1077
disable 'CustomSplashScreen' disable 'CustomSplashScreen'
// https://github.com/organicmaps/organicmaps/issues/3610 // https://github.com/organicmaps/organicmaps/issues/3610

View File

@@ -1 +0,0 @@
../../../../google/java/app/organicmaps/location

View File

@@ -0,0 +1 @@
../../../../../google/java/app/organicmaps/sdk/location

View File

@@ -24,7 +24,7 @@ Sartu komunitatean eta lagundu maparik onena aplikatzen
• Lineaz kanpoko Wikipedia artikuluak • Lineaz kanpoko Wikipedia artikuluak
• Metroaren garraio geruza eta jarraibideak • Metroaren garraio geruza eta jarraibideak
• Arrastoen grabazioa • Arrastoen grabazioa
Markagailuak eta arrastoak esportatu eta inportatu KML, KMZ, GPX formatuetan Laster-markak eta ibilbideak esportatu eta inportatu KML, KMZ, GPX formatuetan
• Gauean erabiltzeko modu iluna • Gauean erabiltzeko modu iluna
• Hobetu mapako datuak guztiontzat oinarrizko editore integratua erabiliz • Hobetu mapako datuak guztiontzat oinarrizko editore integratua erabiliz

View File

@@ -1,32 +0,0 @@
A CoMaps egy közösség által létrehozott, ingyenes és nyílt forráskódú térképalkalmazás, amely az OpenStreetMap térképadatain alapul, és amelyet az átláthatóság, a magánélet védelme és a nonprofit jelleg iránti elkötelezettség erősít. A CoMaps az Organic Maps villája, amely viszont a Maps.ME villája.
A projekt előzményeiről és az irányáról a <b><i>codeberg.org/comaps</i></b> oldalon olvashatsz.
Csatlakozz ott a közösséghez, és segíts a legjobb térképalkalmazás elkészítésében.
• Használd az alkalmazást és terjeszd a hírét
• Adj visszajelzést és jelentsd a problémákat
• Frissítsd és javítsd a térképadatokat az alkalmazásban vagy az OpenStreetMap weboldalán.
‣ <b>Offline-fókuszú:</b> Tervezd meg és navigáld külföldi útad mobilszolgáltatás nélkül, keress útpontokat egy távoli túra során stb. Az alkalmazás minden funkcióját úgy terveztük, hogy internetkapcsolat nélkül is működjön.
‣ <b>A magánélet tiszteletben tartása:</b> Az alkalmazás az adatvédelem szem előtt tartásával készült, nem azonosítja az embereket, nem követi nyomon és nem gyűjt személyes adatokat, és reklámmentes.
‣ <b>Takarékosdik az akkumulátorral és a tárhellyel:</b> Nem meríti le az akkumulátort, mint más navigációs alkalmazások. A jól összeállítot kis méretű térképek értékes helyet takarítanak meg a telefonon.
‣ <b>Egyszerű és szép grafikus felület,</b> nagyszerű és könnyen használható funkciók, amelyek egyszerűen működnek.
‣ <b>Ingyenes és a közösség által készített:</b> A hozzád hasonló emberek segítettek az alkalmazás létrehozásában azáltal, hogy hozzáadtak helyeket az OpenStreetMap térképhez, tesztelték és visszajelzést adtak a funkciókról, valamint hozzájárultak a fejlesztői képességeikkel és a pénzükkel.
‣ <b>Nyílt és átlátható döntéshozatal és pénzügyek, nonprofit és teljesen nyílt forráskódú.</b>
<b>Főbb jellemzők:</b>
• Letölthető részletes térképek olyan helyekkel, amelyek sok kereskedelmi térképen nem állnak rendelkezésre.
• Szabadtéri üzemmód kiemelt túraútvonalakkal, táborhelyekkel, vízforrásokkal, csúcsokkal, szintvonalakkal stb.
• Sétaútvonalak és kerékpárutak
• Érdekes pontok, mint például éttermek, benzinkutak, szállodák, üzletek, látnivalók és még sok minden más
• Keresés név, cím vagy az érdekes pontok kategóriája alapján
• Navigáció hangutasításokkal gyalogláshoz, kerékpározáshoz vagy vezetéshez
• Kedvenc helyeid könyvjelzővel láthatod el egyetlen érintéssel
• Wikipedia szócikkek internetkapcsolat nélkül
• Metró tranzit réteg és irányok
• Útvonal mentése
• Könyvjelzők és nyomvonalak exportálása és importálása KML, KMZ, GPX formátumokban
• Sötét üzemmód az éjszakai használathoz
• Térképadatok javítása mindenki számára egy alapvető beépített szerkesztő segítségével
<b>Itt van a szabadság</b>
Fedezd fel az utadat, navigálj a világban úgy, hogy a magánélet és a közösség kerüljön előtérbe!

View File

@@ -1,4 +1,4 @@
Una app realizzata dalla community gratuita e open-source, basata su OpenStreetMap e sull'impegno alla trasparenza, al rispetto della Privacy e senza scopo di lucro. CoMaps è uno spin-off di Organic Maps, che a sua volta deriva da Maps.ME. Una app sviluppata dalla comunità, gratuita e open-source, basata su OpenStreetMap e sull'impegno alla trasparenza, al rispetto della Privacy senza scopo di lucro. CoMaps è uno spin-off di Organic Maps, che a sua volta deriva da Maps.ME.
Leggi delle ragioni del progetto e della sua direzione futura su <b><i>codeberg.org/comaps</i></b>. Leggi delle ragioni del progetto e della sua direzione futura su <b><i>codeberg.org/comaps</i></b>.
Unisciti alla nostra comunità e aiutaci a creare la migliore app di mappe. Unisciti alla nostra comunità e aiutaci a creare la migliore app di mappe.
@@ -15,7 +15,7 @@ Unisciti alla nostra comunità e aiutaci a creare la migliore app di mappe.
<b>Funzionalità principali</b>: <b>Funzionalità principali</b>:
• Scarica mappe dettagliate di luoghi che non sono disponibili su Google Maps • Scarica mappe dettagliate di luoghi che non sono disponibili su Google Maps
• Modalità Outdoor con percorsi escursionistici, campeggi, sorgenti d'acqua, picchi, dislivelli ed altri punti d'interesse evidenziati • Modalità Outdoor con percorsi escursionistici, campeggi, sorgenti d'acqua, picchi, dislivelli ed altro evidenziati
• Strade pedonali e piste ciclabili • Strade pedonali e piste ciclabili
• Punti d'interesse come ristoranti, stazioni di benzina, hotel, negozi, luoghi turistici e molto altro • Punti d'interesse come ristoranti, stazioni di benzina, hotel, negozi, luoghi turistici e molto altro
• Cerca per nome, indirizzo o categoria • Cerca per nome, indirizzo o categoria

View File

@@ -1 +1 @@
Navigazione semplice - Immergiti nella tua avventura - Realizzato dalla comunità Navigazione semplice - Immergiti nella tua avventura - Sviluppato dalla comunità

View File

@@ -1 +1 @@
CoMaps - Wandel, fiets, rijd offline met privacy CoMaps - Wandel, fiets, rijdt offline met privacy

View File

@@ -1 +0,0 @@
Lett kart navigasjon - Opplev mere på din reise - Drevet av felleskapet

View File

@@ -1 +1 @@
version: 2025.03.02-7-FDroid+25030207 version: 2025.08.13-8-FDroid+25081308

View File

@@ -1,4 +1,4 @@
package app.organicmaps.location; package app.organicmaps.sdk.location;
import static android.Manifest.permission.ACCESS_COARSE_LOCATION; import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
import static android.Manifest.permission.ACCESS_FINE_LOCATION; import static android.Manifest.permission.ACCESS_FINE_LOCATION;
@@ -10,7 +10,6 @@ import android.location.Location;
import android.os.Looper; import android.os.Looper;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.RequiresPermission; import androidx.annotation.RequiresPermission;
import app.organicmaps.sdk.location.BaseLocationProvider;
import app.organicmaps.sdk.util.LocationUtils; import app.organicmaps.sdk.util.LocationUtils;
import app.organicmaps.sdk.util.log.Logger; import app.organicmaps.sdk.util.log.Logger;
import com.google.android.gms.common.api.ApiException; import com.google.android.gms.common.api.ApiException;

View File

@@ -1,25 +1,23 @@
package app.organicmaps.location; package app.organicmaps.sdk.location;
import android.content.Context; import android.content.Context;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import app.organicmaps.sdk.location.AndroidNativeProvider;
import app.organicmaps.sdk.location.BaseLocationProvider;
import app.organicmaps.sdk.location.LocationProviderFactory;
import app.organicmaps.sdk.util.Config; import app.organicmaps.sdk.util.Config;
import app.organicmaps.sdk.util.log.Logger; import app.organicmaps.sdk.util.log.Logger;
import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GoogleApiAvailability; import com.google.android.gms.common.GoogleApiAvailability;
public class LocationProviderFactoryImpl implements LocationProviderFactory public class LocationProviderFactory
{ {
private static final String TAG = LocationProviderFactoryImpl.class.getSimpleName(); private static final String TAG = LocationProviderFactory.class.getSimpleName();
public boolean isGoogleLocationAvailable(@NonNull Context context) public static boolean isGoogleLocationAvailable(@NonNull Context context)
{ {
return GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(context) == ConnectionResult.SUCCESS; return GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(context) == ConnectionResult.SUCCESS;
} }
public BaseLocationProvider getProvider(@NonNull Context context, @NonNull BaseLocationProvider.Listener listener) public static BaseLocationProvider getProvider(@NonNull Context context,
@NonNull BaseLocationProvider.Listener listener)
{ {
if (isGoogleLocationAvailable(context) && Config.useGoogleServices()) if (isGoogleLocationAvailable(context) && Config.useGoogleServices())
{ {

View File

@@ -1 +0,0 @@
../../../../google/java/app/organicmaps/location

View File

@@ -0,0 +1 @@
../../../../../google/java/app/organicmaps/sdk/location

View File

@@ -6,7 +6,7 @@
<!-- Requiring "android.hardware.touchscreen" here breaks DeX mode --> <!-- Requiring "android.hardware.touchscreen" here breaks DeX mode -->
<uses-feature <uses-feature
android:glEsVersion="0x00030000" android:glEsVersion="0x00020000"
android:required="true"/> android:required="true"/>
<uses-feature <uses-feature
android:name="android.hardware.wifi" android:name="android.hardware.wifi"

View File

@@ -10,8 +10,6 @@ import androidx.core.content.ContextCompat;
import app.organicmaps.sdk.Framework; import app.organicmaps.sdk.Framework;
import app.organicmaps.sdk.bookmarks.data.BookmarkManager; import app.organicmaps.sdk.bookmarks.data.BookmarkManager;
import app.organicmaps.sdk.bookmarks.data.ElevationInfo; import app.organicmaps.sdk.bookmarks.data.ElevationInfo;
import app.organicmaps.sdk.bookmarks.data.Track;
import app.organicmaps.sdk.bookmarks.data.TrackStatistics;
import app.organicmaps.util.ThemeUtils; import app.organicmaps.util.ThemeUtils;
import app.organicmaps.util.Utils; import app.organicmaps.util.Utils;
import app.organicmaps.widget.placepage.AxisValueFormatter; import app.organicmaps.widget.placepage.AxisValueFormatter;
@@ -33,16 +31,17 @@ import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
public class ChartController implements OnChartValueSelectedListener public class ChartController implements OnChartValueSelectedListener,
BookmarkManager.OnElevationActivePointChangedListener,
BookmarkManager.OnElevationCurrentPositionChangedListener
{ {
private static final int CHART_Y_LABEL_COUNT = 3; private static final int CHART_Y_LABEL_COUNT = 3;
private static final int CHART_X_LABEL_COUNT = 6; private static final int CHART_X_LABEL_COUNT = 6;
private static final int CHART_ANIMATION_DURATION = 0; private static final int CHART_ANIMATION_DURATION = 1500;
private static final int CHART_FILL_ALPHA = (int) (0.12 * 255); private static final int CHART_FILL_ALPHA = (int) (0.12 * 255);
private static final int CHART_AXIS_GRANULARITY = 100; private static final int CHART_AXIS_GRANULARITY = 100;
private static final float CUBIC_INTENSITY = 0.2f; private static final float CUBIC_INTENSITY = 0.2f;
private static final int CURRENT_POSITION_OUT_OF_TRACK = -1; private static final int CURRENT_POSITION_OUT_OF_TRACK = -1;
private static final String ELEVATION_PROFILE_POINTS = "ELEVATION_PROFILE_POINTS";
@SuppressWarnings("NullableProblems") @SuppressWarnings("NullableProblems")
@NonNull @NonNull
@@ -63,7 +62,6 @@ public class ChartController implements OnChartValueSelectedListener
private final Context mContext; private final Context mContext;
private long mTrackId = Utils.INVALID_ID; private long mTrackId = Utils.INVALID_ID;
private boolean mCurrentPositionOutOfTrack = true; private boolean mCurrentPositionOutOfTrack = true;
private boolean mInformSelectedActivePointToCore = true;
public ChartController(@NonNull Context context) public ChartController(@NonNull Context context)
{ {
@@ -72,6 +70,8 @@ public class ChartController implements OnChartValueSelectedListener
public void initialize(@NonNull View view) public void initialize(@NonNull View view)
{ {
BookmarkManager.INSTANCE.setElevationActivePointChangedListener(this);
BookmarkManager.INSTANCE.setElevationCurrentPositionChangedListener(this);
final Resources resources = mContext.getResources(); final Resources resources = mContext.getResources();
mChart = view.findViewById(R.id.elevation_profile_chart); mChart = view.findViewById(R.id.elevation_profile_chart);
@@ -101,6 +101,13 @@ public class ChartController implements OnChartValueSelectedListener
initAxises(); initAxises();
} }
@SuppressWarnings("unused")
public void destroy()
{
BookmarkManager.INSTANCE.setElevationActivePointChangedListener(null);
BookmarkManager.INSTANCE.setElevationCurrentPositionChangedListener(null);
}
private void highlightChartCurrentLocation() private void highlightChartCurrentLocation()
{ {
mChart.highlightValues(Collections.singletonList(getCurrentPosHighlight()), mChart.highlightValues(Collections.singletonList(getCurrentPosHighlight()),
@@ -135,17 +142,15 @@ public class ChartController implements OnChartValueSelectedListener
mChart.getAxisRight().setEnabled(false); mChart.getAxisRight().setEnabled(false);
} }
public void setData(Track track) public void setData(@NonNull ElevationInfo info)
{ {
mTrackId = track.getTrackId(); mTrackId = info.getId();
ElevationInfo info = track.getElevationInfo();
TrackStatistics stats = track.getTrackStatistics();
List<Entry> values = new ArrayList<>(); List<Entry> values = new ArrayList<>();
for (ElevationInfo.Point point : info.getPoints()) for (ElevationInfo.Point point : info.getPoints())
values.add(new Entry((float) point.getDistance(), point.getAltitude(), point)); values.add(new Entry((float) point.getDistance(), point.getAltitude()));
LineDataSet set = new LineDataSet(values, ELEVATION_PROFILE_POINTS); LineDataSet set = new LineDataSet(values, "Elevation_profile_points");
set.setMode(LineDataSet.Mode.CUBIC_BEZIER); set.setMode(LineDataSet.Mode.CUBIC_BEZIER);
set.setCubicIntensity(CUBIC_INTENSITY); set.setCubicIntensity(CUBIC_INTENSITY);
set.setDrawFilled(true); set.setDrawFilled(true);
@@ -168,8 +173,8 @@ public class ChartController implements OnChartValueSelectedListener
mChart.setData(data); mChart.setData(data);
mChart.animateX(CHART_ANIMATION_DURATION); mChart.animateX(CHART_ANIMATION_DURATION);
mMinAltitude.setText(Framework.nativeFormatAltitude(stats.getMinElevation())); mMinAltitude.setText(Framework.nativeFormatAltitude(info.getMinAltitude()));
mMaxAltitude.setText(Framework.nativeFormatAltitude(stats.getMaxElevation())); mMaxAltitude.setText(Framework.nativeFormatAltitude(info.getMaxAltitude()));
highlightActivePointManually(); highlightActivePointManually();
} }
@@ -187,9 +192,7 @@ public class ChartController implements OnChartValueSelectedListener
if (mTrackId == Utils.INVALID_ID) if (mTrackId == Utils.INVALID_ID)
return; return;
if (mInformSelectedActivePointToCore) BookmarkManager.INSTANCE.setElevationActivePoint(mTrackId, e.getX());
BookmarkManager.INSTANCE.setElevationActivePoint(mTrackId, e.getX(), (ElevationInfo.Point) e.getData());
mInformSelectedActivePointToCore = true;
} }
@NonNull @NonNull
@@ -208,6 +211,7 @@ public class ChartController implements OnChartValueSelectedListener
highlightChartCurrentLocation(); highlightChartCurrentLocation();
} }
@Override
public void onCurrentPositionChanged() public void onCurrentPositionChanged()
{ {
if (mTrackId == Utils.INVALID_ID) if (mTrackId == Utils.INVALID_ID)
@@ -218,6 +222,7 @@ public class ChartController implements OnChartValueSelectedListener
highlightActivePointManually(); highlightActivePointManually();
} }
@Override
public void onElevationActivePointChanged() public void onElevationActivePointChanged()
{ {
if (mTrackId == Utils.INVALID_ID) if (mTrackId == Utils.INVALID_ID)
@@ -229,7 +234,6 @@ public class ChartController implements OnChartValueSelectedListener
private void highlightActivePointManually() private void highlightActivePointManually()
{ {
Highlight highlight = getActivePoint(); Highlight highlight = getActivePoint();
mInformSelectedActivePointToCore = false;
mChart.highlightValue(highlight, true); mChart.highlightValue(highlight, true);
} }

View File

@@ -27,7 +27,6 @@ import androidx.annotation.StringRes;
import androidx.annotation.StyleRes; import androidx.annotation.StyleRes;
import androidx.core.view.ViewCompat; import androidx.core.view.ViewCompat;
import app.organicmaps.base.BaseMwmFragmentActivity; import app.organicmaps.base.BaseMwmFragmentActivity;
import app.organicmaps.downloader.MapManagerHelper;
import app.organicmaps.intent.Factory; import app.organicmaps.intent.Factory;
import app.organicmaps.sdk.Framework; import app.organicmaps.sdk.Framework;
import app.organicmaps.sdk.downloader.CountryItem; import app.organicmaps.sdk.downloader.CountryItem;
@@ -36,7 +35,7 @@ import app.organicmaps.sdk.location.LocationListener;
import app.organicmaps.sdk.util.Config; import app.organicmaps.sdk.util.Config;
import app.organicmaps.sdk.util.ConnectionState; import app.organicmaps.sdk.util.ConnectionState;
import app.organicmaps.sdk.util.StringUtils; import app.organicmaps.sdk.util.StringUtils;
import app.organicmaps.util.UiUtils; import app.organicmaps.sdk.util.UiUtils;
import app.organicmaps.util.Utils; import app.organicmaps.util.Utils;
import app.organicmaps.util.WindowInsetUtils.PaddingInsetsListener; import app.organicmaps.util.WindowInsetUtils.PaddingInsetsListener;
import com.google.android.material.button.MaterialButton; import com.google.android.material.button.MaterialButton;
@@ -117,10 +116,10 @@ public class DownloadResourcesLegacyActivity extends BaseMwmFragmentActivity
private final app.organicmaps.sdk.DownloadResourcesLegacyActivity.Listener mResourcesDownloadListener = private final app.organicmaps.sdk.DownloadResourcesLegacyActivity.Listener mResourcesDownloadListener =
new app.organicmaps.sdk.DownloadResourcesLegacyActivity.Listener() { new app.organicmaps.sdk.DownloadResourcesLegacyActivity.Listener() {
@Override @Override
public void onProgress(final int bytesDownloaded) public void onProgress(final int percent)
{ {
if (!isFinishing()) if (!isFinishing())
mProgress.setProgressCompat(bytesDownloaded, true); mProgress.setProgressCompat(percent, true);
} }
@Override @Override
@@ -151,14 +150,14 @@ public class DownloadResourcesLegacyActivity extends BaseMwmFragmentActivity
switch (item.newStatus) switch (item.newStatus)
{ {
case CountryItem.STATUS_DONE: case CountryItem.STATUS_DONE:
mAreResourcesDownloaded = true; mAreResourcesDownloaded = true;
showMap(); showMap();
return; return;
case CountryItem.STATUS_FAILED: case CountryItem.STATUS_FAILED:
MapManagerHelper.showError(DownloadResourcesLegacyActivity.this, item, null); MapManager.showError(DownloadResourcesLegacyActivity.this, item, null);
return; return;
} }
} }
} }
@@ -253,8 +252,7 @@ public class DownloadResourcesLegacyActivity extends BaseMwmFragmentActivity
setDownloadMessage(bytes); setDownloadMessage(bytes);
mProgress.setMax(bytes); mProgress.setMax(bytes);
// Start progress at 1% according to M3 guidelines mProgress.setProgressCompat(0, true);
mProgress.setProgressCompat(bytes/100, true);
} }
else else
finishFilesDownload(bytes); finishFilesDownload(bytes);
@@ -372,11 +370,10 @@ public class DownloadResourcesLegacyActivity extends BaseMwmFragmentActivity
String fileSizeString = StringUtils.getFileSizeString(this, item.totalSize); String fileSizeString = StringUtils.getFileSizeString(this, item.totalSize);
mTvMessage.setText(getString(R.string.downloading_country_can_proceed, item.name, fileSizeString)); mTvMessage.setText(getString(R.string.downloading_country_can_proceed, item.name, fileSizeString));
mProgress.setMax((int) item.totalSize); mProgress.setMax((int) item.totalSize);
// Start progress at 1% according to M3 guidelines mProgress.setProgressCompat(0, true);
mProgress.setProgressCompat((int) (item.totalSize/100), true);
mCountryDownloadListenerSlot = MapManager.nativeSubscribe(mCountryDownloadListener); mCountryDownloadListenerSlot = MapManager.nativeSubscribe(mCountryDownloadListener);
MapManagerHelper.startDownload(mCurrentCountry); MapManager.startDownload(mCurrentCountry);
setAction(PROCEED_TO_MAP); setAction(PROCEED_TO_MAP);
} }
else else

View File

@@ -23,8 +23,6 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder;
public class MapFragment extends BaseMwmFragment implements View.OnTouchListener, SurfaceHolder.Callback public class MapFragment extends BaseMwmFragment implements View.OnTouchListener, SurfaceHolder.Callback
{ {
private static final String TAG = MapFragment.class.getSimpleName(); private static final String TAG = MapFragment.class.getSimpleName();
@NonNull
private final Map mMap = new Map(DisplayType.Device); private final Map mMap = new Map(DisplayType.Device);
public void updateCompassOffset(int offsetX, int offsetY) public void updateCompassOffset(int offsetX, int offsetY)
@@ -86,8 +84,6 @@ public class MapFragment extends BaseMwmFragment implements View.OnTouchListener
{ {
Logger.d(TAG); Logger.d(TAG);
super.onAttach(context); super.onAttach(context);
mMap.setLocationHelper(MwmApplication.from(requireContext()).getLocationHelper());
mMap.setMapRenderingListener((MapRenderingListener) context); mMap.setMapRenderingListener((MapRenderingListener) context);
mMap.setCallbackUnsupported(this::reportUnsupported); mMap.setCallbackUnsupported(this::reportUnsupported);
} }
@@ -163,24 +159,24 @@ public class MapFragment extends BaseMwmFragment implements View.OnTouchListener
int pointerIndex = event.getActionIndex(); int pointerIndex = event.getActionIndex();
switch (action) switch (action)
{ {
case MotionEvent.ACTION_POINTER_UP -> action = Map.NATIVE_ACTION_UP; case MotionEvent.ACTION_POINTER_UP -> action = Map.NATIVE_ACTION_UP;
case MotionEvent.ACTION_UP -> case MotionEvent.ACTION_UP ->
{ {
action = Map.NATIVE_ACTION_UP; action = Map.NATIVE_ACTION_UP;
pointerIndex = 0; pointerIndex = 0;
} }
case MotionEvent.ACTION_POINTER_DOWN -> action = Map.NATIVE_ACTION_DOWN; case MotionEvent.ACTION_POINTER_DOWN -> action = Map.NATIVE_ACTION_DOWN;
case MotionEvent.ACTION_DOWN -> case MotionEvent.ACTION_DOWN ->
{ {
action = Map.NATIVE_ACTION_DOWN; action = Map.NATIVE_ACTION_DOWN;
pointerIndex = 0; pointerIndex = 0;
} }
case MotionEvent.ACTION_MOVE -> case MotionEvent.ACTION_MOVE ->
{ {
action = Map.NATIVE_ACTION_MOVE; action = Map.NATIVE_ACTION_MOVE;
pointerIndex = Map.INVALID_POINTER_MASK; pointerIndex = Map.INVALID_POINTER_MASK;
} }
case MotionEvent.ACTION_CANCEL -> action = Map.NATIVE_ACTION_CANCEL; case MotionEvent.ACTION_CANCEL -> action = Map.NATIVE_ACTION_CANCEL;
} }
Map.onTouch(action, event, pointerIndex); Map.onTouch(action, event, pointerIndex);
return true; return true;

View File

@@ -12,13 +12,13 @@ import static app.organicmaps.sdk.location.LocationState.FOLLOW;
import static app.organicmaps.sdk.location.LocationState.FOLLOW_AND_ROTATE; import static app.organicmaps.sdk.location.LocationState.FOLLOW_AND_ROTATE;
import static app.organicmaps.sdk.location.LocationState.LOCATION_TAG; import static app.organicmaps.sdk.location.LocationState.LOCATION_TAG;
import static app.organicmaps.sdk.util.PowerManagment.POWER_MANAGEMENT_TAG; import static app.organicmaps.sdk.util.PowerManagment.POWER_MANAGEMENT_TAG;
import static app.organicmaps.sdk.util.Utils.dimen;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.app.Activity; import android.app.Activity;
import android.app.Dialog; import android.app.Dialog;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.content.res.ColorStateList; import android.content.res.ColorStateList;
import android.content.res.Configuration; import android.content.res.Configuration;
@@ -28,6 +28,7 @@ import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.text.TextUtils; import android.text.TextUtils;
import android.text.method.LinkMovementMethod;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View; import android.view.View;
@@ -65,6 +66,7 @@ import app.organicmaps.downloader.OnmapDownloader;
import app.organicmaps.editor.EditorActivity; import app.organicmaps.editor.EditorActivity;
import app.organicmaps.editor.EditorHostFragment; import app.organicmaps.editor.EditorHostFragment;
import app.organicmaps.editor.FeatureCategoryActivity; import app.organicmaps.editor.FeatureCategoryActivity;
import app.organicmaps.editor.OsmLoginActivity;
import app.organicmaps.editor.ReportFragment; import app.organicmaps.editor.ReportFragment;
import app.organicmaps.help.HelpActivity; import app.organicmaps.help.HelpActivity;
import app.organicmaps.intent.Factory; import app.organicmaps.intent.Factory;
@@ -80,6 +82,7 @@ import app.organicmaps.routing.ManageRouteBottomSheet;
import app.organicmaps.routing.NavigationController; import app.organicmaps.routing.NavigationController;
import app.organicmaps.routing.NavigationService; import app.organicmaps.routing.NavigationService;
import app.organicmaps.routing.RoutingBottomMenuListener; import app.organicmaps.routing.RoutingBottomMenuListener;
import app.organicmaps.routing.RoutingController;
import app.organicmaps.routing.RoutingErrorDialogFragment; import app.organicmaps.routing.RoutingErrorDialogFragment;
import app.organicmaps.routing.RoutingPlanFragment; import app.organicmaps.routing.RoutingPlanFragment;
import app.organicmaps.routing.RoutingPlanInplaceController; import app.organicmaps.routing.RoutingPlanInplaceController;
@@ -97,6 +100,7 @@ import app.organicmaps.sdk.display.DisplayType;
import app.organicmaps.sdk.downloader.MapManager; import app.organicmaps.sdk.downloader.MapManager;
import app.organicmaps.sdk.downloader.UpdateInfo; import app.organicmaps.sdk.downloader.UpdateInfo;
import app.organicmaps.sdk.editor.Editor; import app.organicmaps.sdk.editor.Editor;
import app.organicmaps.sdk.editor.OsmOAuth;
import app.organicmaps.sdk.location.LocationHelper; import app.organicmaps.sdk.location.LocationHelper;
import app.organicmaps.sdk.location.LocationListener; import app.organicmaps.sdk.location.LocationListener;
import app.organicmaps.sdk.location.LocationState; import app.organicmaps.sdk.location.LocationState;
@@ -104,7 +108,6 @@ import app.organicmaps.sdk.location.SensorListener;
import app.organicmaps.sdk.location.TrackRecorder; import app.organicmaps.sdk.location.TrackRecorder;
import app.organicmaps.sdk.maplayer.isolines.IsolinesState; import app.organicmaps.sdk.maplayer.isolines.IsolinesState;
import app.organicmaps.sdk.routing.RouteMarkType; import app.organicmaps.sdk.routing.RouteMarkType;
import app.organicmaps.sdk.routing.RoutingController;
import app.organicmaps.sdk.routing.RoutingOptions; import app.organicmaps.sdk.routing.RoutingOptions;
import app.organicmaps.sdk.search.SearchEngine; import app.organicmaps.sdk.search.SearchEngine;
import app.organicmaps.sdk.settings.RoadType; import app.organicmaps.sdk.settings.RoadType;
@@ -112,6 +115,8 @@ import app.organicmaps.sdk.settings.UnitLocale;
import app.organicmaps.sdk.util.Config; import app.organicmaps.sdk.util.Config;
import app.organicmaps.sdk.util.LocationUtils; import app.organicmaps.sdk.util.LocationUtils;
import app.organicmaps.sdk.util.PowerManagment; import app.organicmaps.sdk.util.PowerManagment;
import app.organicmaps.sdk.util.ThemeSwitcher;
import app.organicmaps.sdk.util.UiUtils;
import app.organicmaps.sdk.util.log.Logger; import app.organicmaps.sdk.util.log.Logger;
import app.organicmaps.sdk.widget.placepage.PlacePageData; import app.organicmaps.sdk.widget.placepage.PlacePageData;
import app.organicmaps.search.FloatingSearchToolbarController; import app.organicmaps.search.FloatingSearchToolbarController;
@@ -120,9 +125,7 @@ import app.organicmaps.search.SearchFragment;
import app.organicmaps.settings.DrivingOptionsActivity; import app.organicmaps.settings.DrivingOptionsActivity;
import app.organicmaps.settings.SettingsActivity; import app.organicmaps.settings.SettingsActivity;
import app.organicmaps.util.SharingUtils; import app.organicmaps.util.SharingUtils;
import app.organicmaps.util.ThemeSwitcher;
import app.organicmaps.util.ThemeUtils; import app.organicmaps.util.ThemeUtils;
import app.organicmaps.util.UiUtils;
import app.organicmaps.util.Utils; import app.organicmaps.util.Utils;
import app.organicmaps.util.bottomsheet.MenuBottomSheetFragment; import app.organicmaps.util.bottomsheet.MenuBottomSheetFragment;
import app.organicmaps.util.bottomsheet.MenuBottomSheetItem; import app.organicmaps.util.bottomsheet.MenuBottomSheetItem;
@@ -287,6 +290,7 @@ public class MwmActivity extends BaseMwmFragmentActivity
} }
processIntent(); processIntent();
migrateOAuthCredentials();
} }
/** /**
@@ -341,6 +345,36 @@ public class MwmActivity extends BaseMwmFragmentActivity
} }
} }
private void migrateOAuthCredentials()
{
if (OsmOAuth.containsOAuth1Credentials())
{
// Remove old OAuth v1 secrets
OsmOAuth.clearOAuth1Credentials();
// Notify user to re-login
dismissAlertDialog();
final DialogInterface.OnClickListener navigateToLoginHandler =
(dialog, which) -> startActivity(new Intent(MwmActivity.this, OsmLoginActivity.class));
final int marginBase = getResources().getDimensionPixelSize(R.dimen.margin_base);
final float textSize = getResources().getDimension(R.dimen.line_spacing_extra_1);
final TextView text = new TextView(this);
text.setText(getText(R.string.alert_reauth_message));
text.setPadding(marginBase, marginBase, marginBase, marginBase);
text.setTextSize(textSize);
text.setMovementMethod(LinkMovementMethod.getInstance());
mAlertDialog = new MaterialAlertDialogBuilder(this, R.style.MwmTheme_AlertDialog)
.setTitle(R.string.login_osm)
.setView(text)
.setPositiveButton(R.string.login, navigateToLoginHandler)
.setNegativeButton(R.string.cancel, null)
.setOnDismissListener(dialog -> mAlertDialog = null)
.show();
}
}
private static void checkMeasurementSystem() private static void checkMeasurementSystem()
{ {
UnitLocale.initializeCurrentUnits(); UnitLocale.initializeCurrentUnits();
@@ -458,10 +492,12 @@ public class MwmActivity extends BaseMwmFragmentActivity
@StyleRes @StyleRes
protected int getThemeResourceId(@NonNull String theme) protected int getThemeResourceId(@NonNull String theme)
{ {
if (Config.UiTheme.isDefault(theme)) Context context = getApplicationContext();
if (ThemeUtils.isDefaultTheme(context, theme))
return R.style.MwmTheme_MainActivity; return R.style.MwmTheme_MainActivity;
if (Config.UiTheme.isNight(theme)) if (ThemeUtils.isNightTheme(context, theme))
return R.style.MwmTheme_Night_MainActivity; return R.style.MwmTheme_Night_MainActivity;
return super.getThemeResourceId(theme); return super.getThemeResourceId(theme);
@@ -583,7 +619,7 @@ public class MwmActivity extends BaseMwmFragmentActivity
private void refreshLightStatusBar() private void refreshLightStatusBar()
{ {
UiUtils.setLightStatusBar(this, !(ThemeUtils.isNightTheme() || RoutingController.get().isPlanning() UiUtils.setLightStatusBar(this, !(ThemeUtils.isNightTheme(this) || RoutingController.get().isPlanning()
|| ChoosePositionMode.get() != ChoosePositionMode.None)); || ChoosePositionMode.get() != ChoosePositionMode.None));
} }
@@ -593,7 +629,7 @@ public class MwmActivity extends BaseMwmFragmentActivity
UiUtils.setViewInsetsPaddingBottom(mPointChooser, windowInsets); UiUtils.setViewInsetsPaddingBottom(mPointChooser, windowInsets);
UiUtils.setViewInsetsPaddingNoBottom(mPointChooserToolbar, windowInsets); UiUtils.setViewInsetsPaddingNoBottom(mPointChooserToolbar, windowInsets);
final int trackRecorderOffset = final int trackRecorderOffset =
TrackRecorder.nativeIsTrackRecordingEnabled() ? dimen(this, R.dimen.map_button_size) : 0; TrackRecorder.nativeIsTrackRecordingEnabled() ? UiUtils.dimen(this, R.dimen.map_button_size) : 0;
mNavBarHeight = isFullscreen() ? 0 : windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()).bottom; mNavBarHeight = isFullscreen() ? 0 : windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()).bottom;
// For the first loading, set compass top margin to status bar size // For the first loading, set compass top margin to status bar size
// The top inset will be then be updated by the routing controller // The top inset will be then be updated by the routing controller
@@ -647,29 +683,29 @@ public class MwmActivity extends BaseMwmFragmentActivity
mPointChooser.findViewById(R.id.done).setOnClickListener(v -> { mPointChooser.findViewById(R.id.done).setOnClickListener(v -> {
switch (ChoosePositionMode.get()) switch (ChoosePositionMode.get())
{ {
case Api: case Api:
final Intent apiResult = new Intent(); final Intent apiResult = new Intent();
final double[] center = Framework.nativeGetScreenRectCenter(); final double[] center = Framework.nativeGetScreenRectCenter();
apiResult.putExtra(Const.EXTRA_POINT_LAT, center[0]); apiResult.putExtra(Const.EXTRA_POINT_LAT, center[0]);
apiResult.putExtra(Const.EXTRA_POINT_LON, center[1]); apiResult.putExtra(Const.EXTRA_POINT_LON, center[1]);
apiResult.putExtra(Const.EXTRA_ZOOM_LEVEL, Framework.nativeGetDrawScale()); apiResult.putExtra(Const.EXTRA_ZOOM_LEVEL, Framework.nativeGetDrawScale());
setResult(Activity.RESULT_OK, apiResult); setResult(Activity.RESULT_OK, apiResult);
finish(); finish();
break; break;
case Editor: case Editor:
if (Framework.nativeIsDownloadedMapAtScreenCenter()) if (Framework.nativeIsDownloadedMapAtScreenCenter())
startActivity(new Intent(MwmActivity.this, FeatureCategoryActivity.class)); startActivity(new Intent(MwmActivity.this, FeatureCategoryActivity.class));
else else
{ {
dismissAlertDialog(); dismissAlertDialog();
mAlertDialog = new MaterialAlertDialogBuilder(this, R.style.MwmTheme_AlertDialog) mAlertDialog = new MaterialAlertDialogBuilder(this, R.style.MwmTheme_AlertDialog)
.setTitle(R.string.message_invalid_feature_position) .setTitle(R.string.message_invalid_feature_position)
.setPositiveButton(R.string.ok, null) .setPositiveButton(R.string.ok, null)
.setOnDismissListener(dialog -> mAlertDialog = null) .setOnDismissListener(dialog -> mAlertDialog = null)
.show(); .show();
} }
break; break;
case None: throw new IllegalStateException("Unexpected Framework.nativeGetChoosePositionMode()"); case None: throw new IllegalStateException("Unexpected Framework.nativeGetChoosePositionMode()");
} }
closePositionChooser(); closePositionChooser();
}); });
@@ -736,9 +772,9 @@ public class MwmActivity extends BaseMwmFragmentActivity
final View mapView = mMapFragment.getView(); final View mapView = mMapFragment.getView();
if (mapView != null) if (mapView != null)
{ {
int width = mapView.getWidth(); int width = mapView.getWidth();
int height = mapView.getHeight(); int height = mapView.getHeight();
Framework.nativeSetVisibleRect(0, 0, width, height); Framework.nativeSetVisibleRect(0, 0, width, height);
} }
} }
UiUtils.show(mPointChooser); UiUtils.show(mPointChooser);
@@ -936,24 +972,24 @@ public class MwmActivity extends BaseMwmFragmentActivity
{ {
switch (button) switch (button)
{ {
case zoomIn -> Map.zoomIn(); case zoomIn -> Map.zoomIn();
case zoomOut -> Map.zoomOut(); case zoomOut -> Map.zoomOut();
case myPosition -> case myPosition ->
{ {
Logger.i(LOCATION_TAG, "The location button pressed"); Logger.i(LOCATION_TAG, "The location button pressed");
// Calls onMyPositionModeChanged(mode + 1). // Calls onMyPositionModeChanged(mode + 1).
LocationState.nativeSwitchToNextMode(); LocationState.nativeSwitchToNextMode();
} }
case toggleMapLayer -> toggleMapLayerBottomSheet(); case toggleMapLayer -> toggleMapLayerBottomSheet();
case bookmarks -> showBookmarks(); case bookmarks -> showBookmarks();
case search -> showSearch(""); case search -> showSearch("");
case menu -> case menu ->
{ {
closeFloatingPanels(); closeFloatingPanels();
showBottomSheet(MAIN_MENU_ID); showBottomSheet(MAIN_MENU_ID);
} }
case help -> showHelp(); case help -> showHelp();
case trackRecordingStatus -> showTrackSaveDialog(); case trackRecordingStatus -> showTrackSaveDialog();
} }
} }
@@ -1187,22 +1223,21 @@ public class MwmActivity extends BaseMwmFragmentActivity
private void onIsolinesStateChanged(@NonNull IsolinesState type) private void onIsolinesStateChanged(@NonNull IsolinesState type)
{ {
if (type == IsolinesState.NODATA) if (type != IsolinesState.EXPIREDDATA)
{ {
Toast.makeText(this, R.string.isolines_location_error_dialog, Toast.LENGTH_SHORT).show(); type.activate(this, findViewById(R.id.coordinator), findViewById(R.id.menu_frame));
return;
} }
if (type == IsolinesState.EXPIREDDATA) dismissAlertDialog();
{ mAlertDialog = new MaterialAlertDialogBuilder(this, R.style.MwmTheme_AlertDialog)
mAlertDialog = new MaterialAlertDialogBuilder(this, R.style.MwmTheme_AlertDialog) .setTitle(R.string.downloader_update_maps)
.setTitle(R.string.downloader_update_maps) .setMessage(R.string.isolines_activation_error_dialog)
.setMessage(R.string.isolines_activation_error_dialog) .setPositiveButton(R.string.ok,
.setPositiveButton( (dialog, which) -> startActivity(new Intent(this, DownloaderActivity.class)))
R.string.ok, (dialog, which) -> startActivity(new Intent(this, DownloaderActivity.class))) .setNegativeButton(R.string.cancel, null)
.setNegativeButton(R.string.cancel, null) .setOnDismissListener(dialog -> mAlertDialog = null)
.setOnDismissListener(dialog -> mAlertDialog = null) .show();
.show();
}
} }
@Override @Override
@@ -1647,17 +1682,17 @@ public class MwmActivity extends BaseMwmFragmentActivity
final int orientation = getResources().getConfiguration().orientation; final int orientation = getResources().getConfiguration().orientation;
final boolean isTrackRecordingEnabled = TrackRecorder.nativeIsTrackRecordingEnabled(); final boolean isTrackRecordingEnabled = TrackRecorder.nativeIsTrackRecordingEnabled();
if (isTrackRecordingEnabled && (orientation != Configuration.ORIENTATION_LANDSCAPE)) if (isTrackRecordingEnabled && (orientation != Configuration.ORIENTATION_LANDSCAPE))
offsetY += dimen(this, R.dimen.map_button_size); offsetY += UiUtils.dimen(this, R.dimen.map_button_size);
if (orientation == Configuration.ORIENTATION_LANDSCAPE) if (orientation == Configuration.ORIENTATION_LANDSCAPE)
{ {
if (show) if (show)
{ {
final boolean isSmallScreen = UiUtils.getDisplayTotalHeight(this) < dimen(this, R.dimen.dp_400); final boolean isSmallScreen = UiUtils.getDisplayTotalHeight(this) < UiUtils.dimen(this, R.dimen.dp_400);
if (!isSmallScreen || TrackRecorder.nativeIsTrackRecordingEnabled()) if (!isSmallScreen || TrackRecorder.nativeIsTrackRecordingEnabled())
offsetX += dimen(this, R.dimen.map_button_size); offsetX += UiUtils.dimen(this, R.dimen.map_button_size);
} }
else if (isTrackRecordingEnabled) else if (isTrackRecordingEnabled)
offsetY += dimen(this, R.dimen.map_button_size); offsetY += UiUtils.dimen(this, R.dimen.map_button_size);
} }
updateCompassOffset(offsetY, offsetX); updateCompassOffset(offsetY, offsetX);
} }
@@ -2393,18 +2428,18 @@ public class MwmActivity extends BaseMwmFragmentActivity
{ {
switch (keyCode) switch (keyCode)
{ {
case KeyEvent.KEYCODE_DPAD_DOWN: Map.zoomOut(); return true; case KeyEvent.KEYCODE_DPAD_DOWN: Map.zoomOut(); return true;
case KeyEvent.KEYCODE_DPAD_UP: Map.zoomIn(); return true; case KeyEvent.KEYCODE_DPAD_UP: Map.zoomIn(); return true;
case KeyEvent.KEYCODE_ESCAPE: case KeyEvent.KEYCODE_ESCAPE:
final Intent currIntent = getIntent(); final Intent currIntent = getIntent();
final String backUrl = Framework.nativeGetParsedBackUrl(); final String backUrl = Framework.nativeGetParsedBackUrl();
if (TextUtils.isEmpty(backUrl) || (currIntent != null && Factory.isStartedForApiResult(currIntent))) if (TextUtils.isEmpty(backUrl) || (currIntent != null && Factory.isStartedForApiResult(currIntent)))
{ {
finish(); finish();
return true; return true;
} }
return super.onKeyUp(keyCode, event); return super.onKeyUp(keyCode, event);
default: return super.onKeyUp(keyCode, event); default: return super.onKeyUp(keyCode, event);
} }
} }
@@ -2456,7 +2491,7 @@ public class MwmActivity extends BaseMwmFragmentActivity
if (mCurrentWindowInsets != null) if (mCurrentWindowInsets != null)
{ {
final int offset = mCurrentWindowInsets.getInsets(WindowInsetsCompat.Type.systemBars()).top; final int offset = mCurrentWindowInsets.getInsets(WindowInsetsCompat.Type.systemBars()).top;
updateCompassOffset(offset + dimen(this, R.dimen.map_button_size)); updateCompassOffset(offset + UiUtils.dimen(this, R.dimen.map_button_size));
} }
Toast.makeText(this, R.string.track_recording, Toast.LENGTH_SHORT).show(); Toast.makeText(this, R.string.track_recording, Toast.LENGTH_SHORT).show();
TrackRecordingService.startForegroundService(getApplicationContext()); TrackRecordingService.startForegroundService(getApplicationContext());
@@ -2549,7 +2584,7 @@ public class MwmActivity extends BaseMwmFragmentActivity
items.add(new MenuBottomSheetItem(R.string.download_maps, R.drawable.ic_download, getDownloadMapsCounter(), items.add(new MenuBottomSheetItem(R.string.download_maps, R.drawable.ic_download, getDownloadMapsCounter(),
this::onDownloadMapsOptionSelected)); this::onDownloadMapsOptionSelected));
mDonatesUrl = Utils.getDonateUrl(getApplicationContext()); mDonatesUrl = Config.getDonateUrl(getApplicationContext());
if (!mDonatesUrl.isEmpty()) if (!mDonatesUrl.isEmpty())
items.add(new MenuBottomSheetItem(R.string.donate, R.drawable.ic_donate, this::onDonateOptionSelected)); items.add(new MenuBottomSheetItem(R.string.donate, R.drawable.ic_donate, this::onDonateOptionSelected));

View File

@@ -14,12 +14,11 @@ import androidx.lifecycle.DefaultLifecycleObserver;
import androidx.lifecycle.LifecycleObserver; import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.LifecycleOwner; import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.ProcessLifecycleOwner; import androidx.lifecycle.ProcessLifecycleOwner;
import androidx.preference.PreferenceManager;
import app.organicmaps.background.OsmUploadWork; import app.organicmaps.background.OsmUploadWork;
import app.organicmaps.downloader.DownloaderNotifier; import app.organicmaps.downloader.DownloaderNotifier;
import app.organicmaps.location.LocationProviderFactoryImpl;
import app.organicmaps.location.TrackRecordingService; import app.organicmaps.location.TrackRecordingService;
import app.organicmaps.routing.NavigationService; import app.organicmaps.routing.NavigationService;
import app.organicmaps.routing.RoutingController;
import app.organicmaps.sdk.Map; import app.organicmaps.sdk.Map;
import app.organicmaps.sdk.OrganicMaps; import app.organicmaps.sdk.OrganicMaps;
import app.organicmaps.sdk.display.DisplayManager; import app.organicmaps.sdk.display.DisplayManager;
@@ -29,11 +28,9 @@ import app.organicmaps.sdk.location.SensorHelper;
import app.organicmaps.sdk.location.TrackRecorder; import app.organicmaps.sdk.location.TrackRecorder;
import app.organicmaps.sdk.maplayer.isolines.IsolinesManager; import app.organicmaps.sdk.maplayer.isolines.IsolinesManager;
import app.organicmaps.sdk.maplayer.subway.SubwayManager; import app.organicmaps.sdk.maplayer.subway.SubwayManager;
import app.organicmaps.sdk.routing.RoutingController;
import app.organicmaps.sdk.util.Config; import app.organicmaps.sdk.util.Config;
import app.organicmaps.sdk.util.ConnectionState; import app.organicmaps.sdk.util.ConnectionState;
import app.organicmaps.sdk.util.log.Logger; import app.organicmaps.sdk.util.log.Logger;
import app.organicmaps.util.ThemeSwitcher;
import app.organicmaps.util.Utils; import app.organicmaps.util.Utils;
import java.io.IOException; import java.io.IOException;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
@@ -43,9 +40,6 @@ public class MwmApplication extends Application implements Application.ActivityL
@NonNull @NonNull
private static final String TAG = MwmApplication.class.getSimpleName(); private static final String TAG = MwmApplication.class.getSimpleName();
@NonNull
private final LocationProviderFactoryImpl mLocationProviderFactory = new LocationProviderFactoryImpl();
@SuppressWarnings("NotNullFieldNotInitialized") @SuppressWarnings("NotNullFieldNotInitialized")
@NonNull @NonNull
private OrganicMaps mOrganicMaps; private OrganicMaps mOrganicMaps;
@@ -104,12 +98,6 @@ public class MwmApplication extends Application implements Application.ActivityL
return mOrganicMaps; return mOrganicMaps;
} }
@NonNull
public LocationProviderFactoryImpl getLocationProviderFactory()
{
return mLocationProviderFactory;
}
@NonNull @NonNull
public static MwmApplication from(@NonNull Context context) public static MwmApplication from(@NonNull Context context)
{ {
@@ -130,10 +118,7 @@ public class MwmApplication extends Application implements Application.ActivityL
sInstance = this; sInstance = this;
PreferenceManager.setDefaultValues(this, R.xml.prefs_main, false); mOrganicMaps = new OrganicMaps(getApplicationContext());
mOrganicMaps = new OrganicMaps(getApplicationContext(), BuildConfig.FLAVOR, BuildConfig.APPLICATION_ID,
BuildConfig.VERSION_CODE, BuildConfig.VERSION_NAME,
BuildConfig.FILE_PROVIDER_AUTHORITY, mLocationProviderFactory);
ConnectionState.INSTANCE.initialize(this); ConnectionState.INSTANCE.initialize(this);
@@ -148,8 +133,6 @@ public class MwmApplication extends Application implements Application.ActivityL
public boolean initOrganicMaps(@NonNull Runnable onComplete) throws IOException public boolean initOrganicMaps(@NonNull Runnable onComplete) throws IOException
{ {
return mOrganicMaps.init(() -> { return mOrganicMaps.init(() -> {
ThemeSwitcher.INSTANCE.initialize(this);
ThemeSwitcher.INSTANCE.restart(false);
ProcessLifecycleOwner.get().getLifecycle().addObserver(mProcessLifecycleObserver); ProcessLifecycleOwner.get().getLifecycle().addObserver(mProcessLifecycleObserver);
onComplete.run(); onComplete.run();
}); });

View File

@@ -9,8 +9,7 @@ import androidx.annotation.IntegerRes;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import app.organicmaps.sdk.util.Utils; import app.organicmaps.sdk.util.UiUtils;
import app.organicmaps.util.UiUtils;
import org.chromium.base.ObserverList; import org.chromium.base.ObserverList;
class PanelAnimator class PanelAnimator
@@ -27,7 +26,7 @@ class PanelAnimator
PanelAnimator(MwmActivity activity) PanelAnimator(MwmActivity activity)
{ {
mActivity = activity; mActivity = activity;
mWidth = Utils.dimen(activity.getApplicationContext(), R.dimen.panel_width); mWidth = UiUtils.dimen(activity.getApplicationContext(), R.dimen.panel_width);
mPanel = mActivity.findViewById(R.id.fragment_container); mPanel = mActivity.findViewById(R.id.fragment_container);
mDuration = mActivity.getResources().getInteger(R.integer.anim_panel); mDuration = mActivity.getResources().getInteger(R.integer.anim_panel);
} }

View File

@@ -60,10 +60,11 @@ public class SplashActivity extends AppCompatActivity
{ {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
final String theme = Config.UiTheme.getCurrent(); final Context context = getApplicationContext();
if (Config.UiTheme.isDefault(theme)) final String theme = Config.getCurrentUiTheme(context);
if (ThemeUtils.isDefaultTheme(context, theme))
setTheme(R.style.MwmTheme_Splash); setTheme(R.style.MwmTheme_Splash);
else if (Config.UiTheme.isNight(theme)) else if (ThemeUtils.isNightTheme(context, theme))
setTheme(R.style.MwmTheme_Night_Splash); setTheme(R.style.MwmTheme_Night_Splash);
else else
throw new IllegalArgumentException("Attempt to apply unsupported theme: " + theme); throw new IllegalArgumentException("Attempt to apply unsupported theme: " + theme);

View File

@@ -9,7 +9,7 @@ import android.webkit.WebView;
import android.webkit.WebViewClient; import android.webkit.WebViewClient;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import app.organicmaps.base.OnBackPressListener; import app.organicmaps.base.OnBackPressListener;
import app.organicmaps.util.UiUtils; import app.organicmaps.sdk.util.UiUtils;
public abstract class WebContainerDelegate implements OnBackPressListener public abstract class WebContainerDelegate implements OnBackPressListener
{ {

View File

@@ -1,7 +1,6 @@
package app.organicmaps.backup; package app.organicmaps.backup;
import static app.organicmaps.sdk.util.StorageUtils.isFolderWritable; import static app.organicmaps.sdk.util.StorageUtils.isFolderWritable;
import static app.organicmaps.sdk.util.Utils.dimen;
import static app.organicmaps.settings.BackupSettingsFragment.MAX_BACKUPS_DEFAULT_COUNT; import static app.organicmaps.settings.BackupSettingsFragment.MAX_BACKUPS_DEFAULT_COUNT;
import static app.organicmaps.settings.BackupSettingsFragment.MAX_BACKUPS_KEY; import static app.organicmaps.settings.BackupSettingsFragment.MAX_BACKUPS_KEY;
@@ -16,8 +15,8 @@ import android.text.style.AbsoluteSizeSpan;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.documentfile.provider.DocumentFile; import androidx.documentfile.provider.DocumentFile;
import app.organicmaps.R; import app.organicmaps.R;
import app.organicmaps.sdk.util.UiUtils;
import app.organicmaps.sdk.util.log.Logger; import app.organicmaps.sdk.util.log.Logger;
import app.organicmaps.util.UiUtils;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.ArrayList; import java.util.ArrayList;
@@ -51,14 +50,14 @@ public class BackupUtils
String volumeName; String volumeName;
if ("primary".equalsIgnoreCase(volumeId)) if ("primary".equalsIgnoreCase(volumeId))
volumeName = context.getString(app.organicmaps.sdk.R.string.maps_storage_shared); volumeName = context.getString(R.string.maps_storage_shared);
else else
volumeName = context.getString(app.organicmaps.sdk.R.string.maps_storage_removable); volumeName = context.getString(R.string.maps_storage_removable);
SpannableStringBuilder sb = new SpannableStringBuilder(); SpannableStringBuilder sb = new SpannableStringBuilder();
sb.append(volumeName + ": \n", new AbsoluteSizeSpan(dimen(context, R.dimen.text_size_body_3)), sb.append(volumeName + ": \n", new AbsoluteSizeSpan(UiUtils.dimen(context, R.dimen.text_size_body_3)),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
sb.append("/" + subPath, new AbsoluteSizeSpan(dimen(context, R.dimen.text_size_body_4)), sb.append("/" + subPath, new AbsoluteSizeSpan(UiUtils.dimen(context, R.dimen.text_size_body_4)),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
return sb; return sb;
} }

View File

@@ -62,39 +62,39 @@ public class LocalBackupManager implements BookmarkManager.BookmarksSharingListe
ErrorCode errorCode = null; ErrorCode errorCode = null;
switch (result.getCode()) switch (result.getCode())
{ {
case BookmarkSharingResult.SUCCESS -> case BookmarkSharingResult.SUCCESS ->
{ {
if (!saveBackup(result)) if (!saveBackup(result))
{
Logger.e(TAG, "Failed to save backup. See system log above");
errorCode = ErrorCode.FILE_ERROR;
}
else
{
Logger.i(TAG, "Backup was created and saved successfully");
}
}
case BookmarkSharingResult.EMPTY_CATEGORY ->
{
errorCode = ErrorCode.EMPTY_CATEGORY;
Logger.e(TAG, "Failed to create backup. Category is empty");
}
case BookmarkSharingResult.ARCHIVE_ERROR ->
{
errorCode = ErrorCode.ARCHIVE_ERROR;
Logger.e(TAG, "Failed to create archive of bookmarks");
}
case BookmarkSharingResult.FILE_ERROR ->
{ {
Logger.e(TAG, "Failed to save backup. See system log above");
errorCode = ErrorCode.FILE_ERROR; errorCode = ErrorCode.FILE_ERROR;
Logger.e(TAG, "Failed create file for archive");
} }
else default ->
{ {
Logger.i(TAG, "Backup was created and saved successfully"); errorCode = ErrorCode.UNSUPPORTED;
Logger.e(TAG, "Failed to create backup. Unknown error");
} }
} }
case BookmarkSharingResult.EMPTY_CATEGORY ->
{
errorCode = ErrorCode.EMPTY_CATEGORY;
Logger.e(TAG, "Failed to create backup. Category is empty");
}
case BookmarkSharingResult.ARCHIVE_ERROR ->
{
errorCode = ErrorCode.ARCHIVE_ERROR;
Logger.e(TAG, "Failed to create archive of bookmarks");
}
case BookmarkSharingResult.FILE_ERROR ->
{
errorCode = ErrorCode.FILE_ERROR;
Logger.e(TAG, "Failed create file for archive");
}
default ->
{
errorCode = ErrorCode.UNSUPPORTED;
Logger.e(TAG, "Failed to create backup. Unknown error");
}
}
ErrorCode finalErrorCode = errorCode; ErrorCode finalErrorCode = errorCode;
UiThread.run(() -> { UiThread.run(() -> {

View File

@@ -15,7 +15,7 @@ public class BaseMwmDialogFragment extends DialogFragment
@StyleRes @StyleRes
protected final int getFullscreenTheme() protected final int getFullscreenTheme()
{ {
return ThemeUtils.isNightTheme() ? getFullscreenDarkTheme() : getFullscreenLightTheme(); return ThemeUtils.isNightTheme(requireContext()) ? getFullscreenDarkTheme() : getFullscreenLightTheme();
} }
protected int getStyle() protected int getStyle()

View File

@@ -1,6 +1,7 @@
package app.organicmaps.base; package app.organicmaps.base;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.graphics.Color; import android.graphics.Color;
import android.media.AudioManager; import android.media.AudioManager;
@@ -23,6 +24,7 @@ import app.organicmaps.sdk.util.Config;
import app.organicmaps.sdk.util.concurrency.UiThread; import app.organicmaps.sdk.util.concurrency.UiThread;
import app.organicmaps.sdk.util.log.Logger; import app.organicmaps.sdk.util.log.Logger;
import app.organicmaps.util.RtlUtils; import app.organicmaps.util.RtlUtils;
import app.organicmaps.util.ThemeUtils;
import com.google.android.material.appbar.MaterialToolbar; import com.google.android.material.appbar.MaterialToolbar;
import java.util.Objects; import java.util.Objects;
@@ -38,10 +40,12 @@ public abstract class BaseMwmFragmentActivity extends AppCompatActivity
@StyleRes @StyleRes
protected int getThemeResourceId(@NonNull String theme) protected int getThemeResourceId(@NonNull String theme)
{ {
if (Config.UiTheme.isDefault(theme)) Context context = getApplicationContext();
if (ThemeUtils.isDefaultTheme(context, theme))
return R.style.MwmTheme; return R.style.MwmTheme;
if (Config.UiTheme.isNight(theme)) if (ThemeUtils.isNightTheme(context, theme))
return R.style.MwmTheme_Night; return R.style.MwmTheme_Night;
throw new IllegalArgumentException("Attempt to apply unsupported theme: " + theme); throw new IllegalArgumentException("Attempt to apply unsupported theme: " + theme);
@@ -58,7 +62,7 @@ public abstract class BaseMwmFragmentActivity extends AppCompatActivity
protected final void onCreate(@Nullable Bundle savedInstanceState) protected final void onCreate(@Nullable Bundle savedInstanceState)
{ {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
mThemeName = Config.UiTheme.getCurrent(); mThemeName = Config.getCurrentUiTheme(getApplicationContext());
setTheme(getThemeResourceId(mThemeName)); setTheme(getThemeResourceId(mThemeName));
EdgeToEdge.enable(this, SystemBarStyle.dark(Color.TRANSPARENT)); EdgeToEdge.enable(this, SystemBarStyle.dark(Color.TRANSPARENT));
RtlUtils.manageRtl(this); RtlUtils.manageRtl(this);
@@ -118,7 +122,7 @@ public abstract class BaseMwmFragmentActivity extends AppCompatActivity
public void onPostResume() public void onPostResume()
{ {
super.onPostResume(); super.onPostResume();
if (!mThemeName.equals(Config.UiTheme.getCurrent())) if (!mThemeName.equals(Config.getCurrentUiTheme(getApplicationContext())))
{ {
// Workaround described in https://code.google.com/p/android/issues/detail?id=93731 // Workaround described in https://code.google.com/p/android/issues/detail?id=93731
UiThread.runLater(this::recreate); UiThread.runLater(this::recreate);

View File

@@ -14,7 +14,7 @@ import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import app.organicmaps.R; import app.organicmaps.R;
import app.organicmaps.util.UiUtils; import app.organicmaps.sdk.util.UiUtils;
import app.organicmaps.util.Utils; import app.organicmaps.util.Utils;
import app.organicmaps.util.WindowInsetUtils.ScrollableContentInsetsListener; import app.organicmaps.util.WindowInsetUtils.ScrollableContentInsetsListener;
import app.organicmaps.widget.PlaceholderView; import app.organicmaps.widget.PlaceholderView;

View File

@@ -10,7 +10,7 @@ import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentFactory; import androidx.fragment.app.FragmentFactory;
import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentManager;
import app.organicmaps.R; import app.organicmaps.R;
import app.organicmaps.util.UiUtils; import app.organicmaps.sdk.util.UiUtils;
import app.organicmaps.util.WindowInsetUtils.PaddingInsetsListener; import app.organicmaps.util.WindowInsetUtils.PaddingInsetsListener;
import com.google.android.material.appbar.MaterialToolbar; import com.google.android.material.appbar.MaterialToolbar;

View File

@@ -42,7 +42,7 @@ public class BookmarkCategoriesActivity extends BaseToolbarActivity
@StyleRes @StyleRes
public int getThemeResourceId(@NonNull String theme) public int getThemeResourceId(@NonNull String theme)
{ {
return ThemeUtils.getWindowBgThemeResourceId(theme); return ThemeUtils.getWindowBgThemeResourceId(getApplicationContext(), theme);
} }
@Override @Override

View File

@@ -66,47 +66,47 @@ public class BookmarkCategoriesAdapter extends BaseBookmarkCategoryAdapter<Recyc
LayoutInflater inflater = LayoutInflater.from(parent.getContext()); LayoutInflater inflater = LayoutInflater.from(parent.getContext());
switch (viewType) switch (viewType)
{ {
case TYPE_ACTION_HEADER -> case TYPE_ACTION_HEADER ->
{ {
View header = inflater.inflate(R.layout.item_bookmark_group_list_header, parent, false); View header = inflater.inflate(R.layout.item_bookmark_group_list_header, parent, false);
return new HeaderViewHolder(header); return new HeaderViewHolder(header);
} }
case TYPE_CATEGORY_ITEM -> case TYPE_CATEGORY_ITEM ->
{ {
View view = inflater.inflate(R.layout.item_bookmark_category, parent, false); View view = inflater.inflate(R.layout.item_bookmark_category, parent, false);
final CategoryViewHolder holder = new CategoryViewHolder(view); final CategoryViewHolder holder = new CategoryViewHolder(view);
view.setOnClickListener(new CategoryItemClickListener(holder)); view.setOnClickListener(new CategoryItemClickListener(holder));
view.setOnLongClickListener(new LongClickListener(holder)); view.setOnLongClickListener(new LongClickListener(holder));
return holder; return holder;
} }
case TYPE_ACTION_ADD -> case TYPE_ACTION_ADD ->
{ {
View item = inflater.inflate(R.layout.item_bookmark_button, parent, false); View item = inflater.inflate(R.layout.item_bookmark_button, parent, false);
item.setOnClickListener(v -> { item.setOnClickListener(v -> {
if (mCategoryListCallback != null) if (mCategoryListCallback != null)
mCategoryListCallback.onAddButtonClick(); mCategoryListCallback.onAddButtonClick();
}); });
return new Holders.GeneralViewHolder(item); return new Holders.GeneralViewHolder(item);
} }
case TYPE_ACTION_IMPORT -> case TYPE_ACTION_IMPORT ->
{ {
View item = inflater.inflate(R.layout.item_bookmark_button, parent, false); View item = inflater.inflate(R.layout.item_bookmark_button, parent, false);
item.setOnClickListener(v -> { item.setOnClickListener(v -> {
if (mCategoryListCallback != null) if (mCategoryListCallback != null)
mCategoryListCallback.onImportButtonClick(); mCategoryListCallback.onImportButtonClick();
}); });
return new Holders.GeneralViewHolder(item); return new Holders.GeneralViewHolder(item);
} }
case TYPE_ACTION_EXPORT_ALL_AS_KMZ -> case TYPE_ACTION_EXPORT_ALL_AS_KMZ ->
{ {
View item = inflater.inflate(R.layout.item_bookmark_button, parent, false); View item = inflater.inflate(R.layout.item_bookmark_button, parent, false);
item.setOnClickListener(v -> { item.setOnClickListener(v -> {
if (mCategoryListCallback != null) if (mCategoryListCallback != null)
mCategoryListCallback.onExportButtonClick(); mCategoryListCallback.onExportButtonClick();
}); });
return new Holders.GeneralViewHolder(item); return new Holders.GeneralViewHolder(item);
} }
default -> throw new AssertionError("Invalid item type: " + viewType); default -> throw new AssertionError("Invalid item type: " + viewType);
} }
} }
@@ -116,44 +116,44 @@ public class BookmarkCategoriesAdapter extends BaseBookmarkCategoryAdapter<Recyc
int type = getItemViewType(position); int type = getItemViewType(position);
switch (type) switch (type)
{ {
case TYPE_ACTION_HEADER -> case TYPE_ACTION_HEADER ->
{ {
HeaderViewHolder headerViewHolder = (HeaderViewHolder) holder; HeaderViewHolder headerViewHolder = (HeaderViewHolder) holder;
headerViewHolder.setAction(mMassOperationAction, BookmarkManager.INSTANCE.areAllCategoriesInvisible()); headerViewHolder.setAction(mMassOperationAction, BookmarkManager.INSTANCE.areAllCategoriesInvisible());
headerViewHolder.getText().setText(R.string.bookmark_lists); headerViewHolder.getText().setText(R.string.bookmark_lists);
} }
case TYPE_CATEGORY_ITEM -> case TYPE_CATEGORY_ITEM ->
{ {
final BookmarkCategory category = getCategoryByPosition(toCategoryPosition(position)); final BookmarkCategory category = getCategoryByPosition(toCategoryPosition(position));
CategoryViewHolder categoryHolder = (CategoryViewHolder) holder; CategoryViewHolder categoryHolder = (CategoryViewHolder) holder;
categoryHolder.setEntity(category); categoryHolder.setEntity(category);
categoryHolder.setName(category.getName()); categoryHolder.setName(category.getName());
categoryHolder.setSize(); categoryHolder.setSize();
categoryHolder.setVisibilityState(category.isVisible()); categoryHolder.setVisibilityState(category.isVisible());
ToggleVisibilityClickListener visibilityListener = new ToggleVisibilityClickListener(categoryHolder); ToggleVisibilityClickListener visibilityListener = new ToggleVisibilityClickListener(categoryHolder);
categoryHolder.setVisibilityListener(visibilityListener); categoryHolder.setVisibilityListener(visibilityListener);
CategoryItemMoreClickListener moreClickListener = new CategoryItemMoreClickListener(categoryHolder); CategoryItemMoreClickListener moreClickListener = new CategoryItemMoreClickListener(categoryHolder);
categoryHolder.setMoreButtonClickListener(moreClickListener); categoryHolder.setMoreButtonClickListener(moreClickListener);
} }
case TYPE_ACTION_ADD -> case TYPE_ACTION_ADD ->
{ {
Holders.GeneralViewHolder generalViewHolder = (Holders.GeneralViewHolder) holder; Holders.GeneralViewHolder generalViewHolder = (Holders.GeneralViewHolder) holder;
generalViewHolder.getImage().setImageResource(R.drawable.ic_add_list); generalViewHolder.getImage().setImageResource(R.drawable.ic_add_list);
generalViewHolder.getText().setText(R.string.bookmarks_create_new_group); generalViewHolder.getText().setText(R.string.bookmarks_create_new_group);
} }
case TYPE_ACTION_IMPORT -> case TYPE_ACTION_IMPORT ->
{ {
Holders.GeneralViewHolder generalViewHolder = (Holders.GeneralViewHolder) holder; Holders.GeneralViewHolder generalViewHolder = (Holders.GeneralViewHolder) holder;
generalViewHolder.getImage().setImageResource(R.drawable.ic_import); generalViewHolder.getImage().setImageResource(R.drawable.ic_import);
generalViewHolder.getText().setText(R.string.bookmarks_import); generalViewHolder.getText().setText(R.string.bookmarks_import);
} }
case TYPE_ACTION_EXPORT_ALL_AS_KMZ -> case TYPE_ACTION_EXPORT_ALL_AS_KMZ ->
{ {
Holders.GeneralViewHolder generalViewHolder = (Holders.GeneralViewHolder) holder; Holders.GeneralViewHolder generalViewHolder = (Holders.GeneralViewHolder) holder;
generalViewHolder.getImage().setImageResource(R.drawable.ic_export); generalViewHolder.getImage().setImageResource(R.drawable.ic_export);
generalViewHolder.getText().setText(R.string.bookmarks_export); generalViewHolder.getText().setText(R.string.bookmarks_export);
} }
default -> throw new AssertionError("Invalid item type: " + type); default -> throw new AssertionError("Invalid item type: " + type);
} }
} }

View File

@@ -11,7 +11,7 @@ import app.organicmaps.R;
import app.organicmaps.adapter.OnItemClickListener; import app.organicmaps.adapter.OnItemClickListener;
import app.organicmaps.sdk.bookmarks.data.BookmarkCategory; import app.organicmaps.sdk.bookmarks.data.BookmarkCategory;
import app.organicmaps.sdk.bookmarks.data.BookmarkManager; import app.organicmaps.sdk.bookmarks.data.BookmarkManager;
import app.organicmaps.util.UiUtils; import app.organicmaps.sdk.util.UiUtils;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.util.List; import java.util.List;

View File

@@ -41,7 +41,7 @@ public class BookmarkListActivity extends BaseToolbarActivity
@StyleRes @StyleRes
public int getThemeResourceId(@NonNull String theme) public int getThemeResourceId(@NonNull String theme)
{ {
return ThemeUtils.getCardBgThemeResourceId(theme); return ThemeUtils.getCardBgThemeResourceId(getApplicationContext(), theme);
} }
@Override @Override

View File

@@ -442,37 +442,37 @@ public class BookmarkListAdapter extends RecyclerView.Adapter<Holders.BaseBookma
Holders.BaseBookmarkHolder holder = null; Holders.BaseBookmarkHolder holder = null;
switch (viewType) switch (viewType)
{ {
case TYPE_TRACK: case TYPE_TRACK:
Holders.TrackViewHolder trackHolder = Holders.TrackViewHolder trackHolder =
new Holders.TrackViewHolder(inflater.inflate(R.layout.item_track, parent, false)); new Holders.TrackViewHolder(inflater.inflate(R.layout.item_track, parent, false));
trackHolder.setOnClickListener(mClickListener); trackHolder.setOnClickListener(mClickListener);
trackHolder.setOnLongClickListener(mLongClickListener); trackHolder.setOnLongClickListener(mLongClickListener);
trackHolder.setTrackIconClickListener(mIconClickListener); trackHolder.setTrackIconClickListener(mIconClickListener);
trackHolder.setMoreButtonClickListener(mMoreClickListener); trackHolder.setMoreButtonClickListener(mMoreClickListener);
holder = trackHolder; holder = trackHolder;
break; break;
case TYPE_BOOKMARK: case TYPE_BOOKMARK:
Holders.BookmarkViewHolder bookmarkHolder = Holders.BookmarkViewHolder bookmarkHolder =
new Holders.BookmarkViewHolder(inflater.inflate(R.layout.item_bookmark, parent, false)); new Holders.BookmarkViewHolder(inflater.inflate(R.layout.item_bookmark, parent, false));
bookmarkHolder.setOnClickListener(mClickListener); bookmarkHolder.setOnClickListener(mClickListener);
bookmarkHolder.setOnLongClickListener(mLongClickListener); bookmarkHolder.setOnLongClickListener(mLongClickListener);
holder = bookmarkHolder; holder = bookmarkHolder;
break; break;
case TYPE_SECTION: case TYPE_SECTION:
MaterialTextView tv = (MaterialTextView) inflater.inflate(R.layout.item_category_title, parent, false); MaterialTextView tv = (MaterialTextView) inflater.inflate(R.layout.item_category_title, parent, false);
holder = new Holders.SectionViewHolder(tv); holder = new Holders.SectionViewHolder(tv);
break; break;
case TYPE_DESC: case TYPE_DESC:
View desc = inflater.inflate(R.layout.item_category_description, parent, false); View desc = inflater.inflate(R.layout.item_category_description, parent, false);
MaterialTextView moreBtn = desc.findViewById(R.id.more_btn); MaterialTextView moreBtn = desc.findViewById(R.id.more_btn);
MaterialTextView text = desc.findViewById(R.id.text); MaterialTextView text = desc.findViewById(R.id.text);
MaterialTextView title = desc.findViewById(R.id.title); MaterialTextView title = desc.findViewById(R.id.title);
setMoreButtonVisibility(text, moreBtn); setMoreButtonVisibility(text, moreBtn);
holder = new Holders.DescriptionViewHolder(desc, mSectionsDataSource.getCategory()); holder = new Holders.DescriptionViewHolder(desc, mSectionsDataSource.getCategory());
text.setOnClickListener(v -> onMoreButtonClicked(text, moreBtn)); text.setOnClickListener(v -> onMoreButtonClicked(text, moreBtn));
moreBtn.setOnClickListener(v -> onMoreButtonClicked(text, moreBtn)); moreBtn.setOnClickListener(v -> onMoreButtonClicked(text, moreBtn));
title.setOnClickListener(v -> onMoreButtonClicked(text, moreBtn)); title.setOnClickListener(v -> onMoreButtonClicked(text, moreBtn));
break; break;
} }
if (holder == null) if (holder == null)

View File

@@ -33,15 +33,15 @@ import app.organicmaps.sdk.bookmarks.data.BookmarkInfo;
import app.organicmaps.sdk.bookmarks.data.BookmarkManager; import app.organicmaps.sdk.bookmarks.data.BookmarkManager;
import app.organicmaps.sdk.bookmarks.data.BookmarkSharingResult; import app.organicmaps.sdk.bookmarks.data.BookmarkSharingResult;
import app.organicmaps.sdk.bookmarks.data.CategoryDataSource; import app.organicmaps.sdk.bookmarks.data.CategoryDataSource;
import app.organicmaps.sdk.bookmarks.data.Icon;
import app.organicmaps.sdk.bookmarks.data.KmlFileType; import app.organicmaps.sdk.bookmarks.data.KmlFileType;
import app.organicmaps.sdk.bookmarks.data.PredefinedColors;
import app.organicmaps.sdk.bookmarks.data.SortedBlock; import app.organicmaps.sdk.bookmarks.data.SortedBlock;
import app.organicmaps.sdk.bookmarks.data.Track; import app.organicmaps.sdk.bookmarks.data.Track;
import app.organicmaps.sdk.search.BookmarkSearchListener; import app.organicmaps.sdk.search.BookmarkSearchListener;
import app.organicmaps.sdk.search.SearchEngine; import app.organicmaps.sdk.search.SearchEngine;
import app.organicmaps.sdk.util.UiUtils;
import app.organicmaps.util.Graphics; import app.organicmaps.util.Graphics;
import app.organicmaps.util.SharingUtils; import app.organicmaps.util.SharingUtils;
import app.organicmaps.util.UiUtils;
import app.organicmaps.util.Utils; import app.organicmaps.util.Utils;
import app.organicmaps.util.WindowInsetUtils; import app.organicmaps.util.WindowInsetUtils;
import app.organicmaps.util.bottomsheet.MenuBottomSheetFragment; import app.organicmaps.util.bottomsheet.MenuBottomSheetFragment;
@@ -551,12 +551,12 @@ public class BookmarksListFragment extends BaseMwmRecyclerFragment<ConcatAdapter
switch (adapter.getItemViewType(position)) switch (adapter.getItemViewType(position))
{ {
case BookmarkListAdapter.TYPE_SECTION, BookmarkListAdapter.TYPE_DESC -> case BookmarkListAdapter.TYPE_SECTION, BookmarkListAdapter.TYPE_DESC ->
{ {
return; return;
} }
case BookmarkListAdapter.TYPE_BOOKMARK -> onBookmarkClicked(position, intent, adapter); case BookmarkListAdapter.TYPE_BOOKMARK -> onBookmarkClicked(position, intent, adapter);
case BookmarkListAdapter.TYPE_TRACK -> onTrackClicked(position, intent, adapter); case BookmarkListAdapter.TYPE_TRACK -> onTrackClicked(position, intent, adapter);
} }
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
@@ -592,7 +592,7 @@ public class BookmarksListFragment extends BaseMwmRecyclerFragment<ConcatAdapter
if (mTrack == null) if (mTrack == null)
return; return;
final Bundle args = new Bundle(); final Bundle args = new Bundle();
args.putInt(BookmarkColorDialogFragment.ICON_COLOR, PredefinedColors.getPredefinedColorIndex(mTrack.getColor())); args.putInt(BookmarkColorDialogFragment.ICON_TYPE, Icon.getColorPosition(mTrack.getColor()));
final FragmentManager manager = getChildFragmentManager(); final FragmentManager manager = getChildFragmentManager();
String className = BookmarkColorDialogFragment.class.getName(); String className = BookmarkColorDialogFragment.class.getName();
final FragmentFactory factory = manager.getFragmentFactory(); final FragmentFactory factory = manager.getFragmentFactory();
@@ -601,7 +601,7 @@ public class BookmarksListFragment extends BaseMwmRecyclerFragment<ConcatAdapter
dialogFragment.setArguments(args); dialogFragment.setArguments(args);
dialogFragment.setOnColorSetListener((colorPos) -> { dialogFragment.setOnColorSetListener((colorPos) -> {
int from = mTrack.getColor(); int from = mTrack.getColor();
int to = PredefinedColors.getColor(colorPos); int to = BookmarkManager.ICONS.get(colorPos).argb();
if (from == to) if (from == to)
return; return;
BookmarkManager.INSTANCE.changeTrackColor(mTrack.getTrackId(), to); BookmarkManager.INSTANCE.changeTrackColor(mTrack.getTrackId(), to);
@@ -621,22 +621,22 @@ public class BookmarksListFragment extends BaseMwmRecyclerFragment<ConcatAdapter
switch (type) switch (type)
{ {
case BookmarkListAdapter.TYPE_SECTION: case BookmarkListAdapter.TYPE_SECTION:
case BookmarkListAdapter.TYPE_DESC: case BookmarkListAdapter.TYPE_DESC:
// Do nothing here? // Do nothing here?
break; break;
case BookmarkListAdapter.TYPE_BOOKMARK: case BookmarkListAdapter.TYPE_BOOKMARK:
final BookmarkInfo bookmark = (BookmarkInfo) adapter.getItem(mSelectedPosition); final BookmarkInfo bookmark = (BookmarkInfo) adapter.getItem(mSelectedPosition);
MenuBottomSheetFragment.newInstance(BOOKMARKS_MENU_ID, bookmark.getName()) MenuBottomSheetFragment.newInstance(BOOKMARKS_MENU_ID, bookmark.getName())
.show(getChildFragmentManager(), BOOKMARKS_MENU_ID); .show(getChildFragmentManager(), BOOKMARKS_MENU_ID);
break; break;
case BookmarkListAdapter.TYPE_TRACK: case BookmarkListAdapter.TYPE_TRACK:
final Track track = (Track) adapter.getItem(mSelectedPosition); final Track track = (Track) adapter.getItem(mSelectedPosition);
MenuBottomSheetFragment.newInstance(TRACK_MENU_ID, track.getName()) MenuBottomSheetFragment.newInstance(TRACK_MENU_ID, track.getName())
.show(getChildFragmentManager(), TRACK_MENU_ID); .show(getChildFragmentManager(), TRACK_MENU_ID);
break; break;
} }
} }
@@ -843,19 +843,19 @@ public class BookmarksListFragment extends BaseMwmRecyclerFragment<ConcatAdapter
{ {
switch (id) switch (id)
{ {
case BOOKMARKS_MENU_ID -> case BOOKMARKS_MENU_ID ->
{ {
return getBookmarkMenuItems(); return getBookmarkMenuItems();
} }
case TRACK_MENU_ID -> case TRACK_MENU_ID ->
{ {
final Track track = (Track) getBookmarkListAdapter().getItem(mSelectedPosition); final Track track = (Track) getBookmarkListAdapter().getItem(mSelectedPosition);
return getTrackMenuItems(track); return getTrackMenuItems(track);
} }
case OPTIONS_MENU_ID -> case OPTIONS_MENU_ID ->
{ {
return getOptionsMenuItems(); return getOptionsMenuItems();
} }
} }
return null; return null;
} }

View File

@@ -57,27 +57,27 @@ public enum BookmarksSharingHelper
switch (result.getCode()) switch (result.getCode())
{ {
case BookmarkSharingResult.SUCCESS -> case BookmarkSharingResult.SUCCESS ->
SharingUtils.shareBookmarkFile(context, launcher, result.getSharingPath(), result.getMimeType()); SharingUtils.shareBookmarkFile(context, launcher, result.getSharingPath(), result.getMimeType());
case BookmarkSharingResult.EMPTY_CATEGORY -> case BookmarkSharingResult.EMPTY_CATEGORY ->
new MaterialAlertDialogBuilder(context, R.style.MwmTheme_AlertDialog) new MaterialAlertDialogBuilder(context, R.style.MwmTheme_AlertDialog)
.setTitle(R.string.bookmarks_error_title_share_empty) .setTitle(R.string.bookmarks_error_title_share_empty)
.setMessage(R.string.bookmarks_error_message_share_empty) .setMessage(R.string.bookmarks_error_message_share_empty)
.setPositiveButton(R.string.ok, null) .setPositiveButton(R.string.ok, null)
.show(); .show();
case BookmarkSharingResult.ARCHIVE_ERROR, BookmarkSharingResult.FILE_ERROR -> case BookmarkSharingResult.ARCHIVE_ERROR, BookmarkSharingResult.FILE_ERROR ->
{ {
new MaterialAlertDialogBuilder(context, R.style.MwmTheme_AlertDialog) new MaterialAlertDialogBuilder(context, R.style.MwmTheme_AlertDialog)
.setTitle(R.string.dialog_routing_system_error) .setTitle(R.string.dialog_routing_system_error)
.setMessage(R.string.bookmarks_error_message_share_general) .setMessage(R.string.bookmarks_error_message_share_general)
.setPositiveButton(R.string.ok, null) .setPositiveButton(R.string.ok, null)
.show(); .show();
List<String> names = new ArrayList<>(); List<String> names = new ArrayList<>();
for (long categoryId : result.getCategoriesIds()) for (long categoryId : result.getCategoriesIds())
names.add(BookmarkManager.INSTANCE.getCategoryById(categoryId).getName()); names.add(BookmarkManager.INSTANCE.getCategoryById(categoryId).getName());
Logger.e(TAG, "Failed to share bookmark categories " + names + ", error code: " + result.getCode()); Logger.e(TAG, "Failed to share bookmark categories " + names + ", error code: " + result.getCode());
} }
default -> throw new AssertionError("Unsupported bookmark sharing code: " + result.getCode()); default -> throw new AssertionError("Unsupported bookmark sharing code: " + result.getCode());
} }
} }

View File

@@ -13,7 +13,7 @@ import androidx.fragment.app.FragmentManager;
import app.organicmaps.R; import app.organicmaps.R;
import app.organicmaps.base.BaseMwmDialogFragment; import app.organicmaps.base.BaseMwmDialogFragment;
import app.organicmaps.sdk.bookmarks.data.BookmarkManager; import app.organicmaps.sdk.bookmarks.data.BookmarkManager;
import app.organicmaps.util.UiUtils; import app.organicmaps.sdk.util.UiUtils;
public class ChooseBookmarksSortingTypeFragment public class ChooseBookmarksSortingTypeFragment
extends BaseMwmDialogFragment implements RadioGroup.OnCheckedChangeListener extends BaseMwmDialogFragment implements RadioGroup.OnCheckedChangeListener
@@ -64,10 +64,10 @@ public class ChooseBookmarksSortingTypeFragment
{ {
switch (sortingType) switch (sortingType)
{ {
case BookmarkManager.SORT_BY_TYPE: return R.id.sort_by_type; case BookmarkManager.SORT_BY_TYPE: return R.id.sort_by_type;
case BookmarkManager.SORT_BY_DISTANCE: return R.id.sort_by_distance; case BookmarkManager.SORT_BY_DISTANCE: return R.id.sort_by_distance;
case BookmarkManager.SORT_BY_TIME: return R.id.sort_by_time; case BookmarkManager.SORT_BY_TIME: return R.id.sort_by_time;
case BookmarkManager.SORT_BY_NAME: return R.id.sort_by_name; case BookmarkManager.SORT_BY_NAME: return R.id.sort_by_name;
} }
} }
return R.id.sort_by_default; return R.id.sort_by_default;

View File

@@ -18,8 +18,8 @@ import app.organicmaps.sdk.bookmarks.data.BookmarkInfo;
import app.organicmaps.sdk.bookmarks.data.BookmarkManager; import app.organicmaps.sdk.bookmarks.data.BookmarkManager;
import app.organicmaps.sdk.bookmarks.data.IconClickListener; import app.organicmaps.sdk.bookmarks.data.IconClickListener;
import app.organicmaps.sdk.bookmarks.data.Track; import app.organicmaps.sdk.bookmarks.data.Track;
import app.organicmaps.sdk.util.UiUtils;
import app.organicmaps.util.Graphics; import app.organicmaps.util.Graphics;
import app.organicmaps.util.UiUtils;
import app.organicmaps.util.Utils; import app.organicmaps.util.Utils;
import app.organicmaps.widget.recycler.RecyclerClickListener; import app.organicmaps.widget.recycler.RecyclerClickListener;
import app.organicmaps.widget.recycler.RecyclerLongClickListener; import app.organicmaps.widget.recycler.RecyclerLongClickListener;

View File

@@ -6,26 +6,19 @@ import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.ArrayAdapter; import android.widget.ArrayAdapter;
import androidx.annotation.DrawableRes;
import app.organicmaps.R; import app.organicmaps.R;
import app.organicmaps.sdk.bookmarks.data.PredefinedColors; import app.organicmaps.sdk.bookmarks.data.Icon;
import app.organicmaps.util.Graphics; import app.organicmaps.util.Graphics;
import com.google.android.material.imageview.ShapeableImageView; import com.google.android.material.imageview.ShapeableImageView;
import java.util.List; import java.util.List;
import java.util.Objects;
public class ColorsAdapter extends ArrayAdapter<Integer> public class IconsAdapter extends ArrayAdapter<Icon>
{ {
@PredefinedColors.Color
private int mCheckedIconColor; private int mCheckedIconColor;
@DrawableRes public IconsAdapter(Context context, List<Icon> list)
private final int mIconResId;
public ColorsAdapter(Context context, List<Integer> list, @DrawableRes int iconResId)
{ {
super(context, 0, 0, list); super(context, 0, 0, list);
mIconResId = iconResId;
} }
@Override @Override
@@ -42,19 +35,19 @@ public class ColorsAdapter extends ArrayAdapter<Integer>
else else
holder = (SpinnerViewHolder) convertView.getTag(); holder = (SpinnerViewHolder) convertView.getTag();
@PredefinedColors.Color final Icon icon = getItem(position);
final int color = Objects.requireNonNull(getItem(position));
Drawable circle; Drawable circle;
if (color == mCheckedIconColor) if (icon.getColor() == mCheckedIconColor)
{ {
circle = Graphics.drawCircleAndImage(PredefinedColors.getColor(mCheckedIconColor), R.dimen.track_circle_size, circle = Graphics.drawCircleAndImage(getItem(position).argb(), R.dimen.track_circle_size,
mIconResId, R.dimen.bookmark_icon_size, getContext()); app.organicmaps.sdk.R.drawable.ic_bookmark_none, R.dimen.bookmark_icon_size,
getContext());
} }
else else
{ {
circle = Graphics.drawCircle(PredefinedColors.getColor(color), R.dimen.select_color_circle_size, circle =
getContext().getResources()); Graphics.drawCircle(getItem(position).argb(), R.dimen.select_color_circle_size, getContext().getResources());
} }
holder.icon.setImageDrawable(circle); holder.icon.setImageDrawable(circle);
return convertView; return convertView;

View File

@@ -26,6 +26,7 @@ import app.organicmaps.car.util.CurrentCountryChangedListener;
import app.organicmaps.car.util.IntentUtils; import app.organicmaps.car.util.IntentUtils;
import app.organicmaps.car.util.ThemeUtils; import app.organicmaps.car.util.ThemeUtils;
import app.organicmaps.car.util.UserActionRequired; import app.organicmaps.car.util.UserActionRequired;
import app.organicmaps.routing.RoutingController;
import app.organicmaps.sdk.Framework; import app.organicmaps.sdk.Framework;
import app.organicmaps.sdk.PlacePageActivationListener; import app.organicmaps.sdk.PlacePageActivationListener;
import app.organicmaps.sdk.bookmarks.data.MapObject; import app.organicmaps.sdk.bookmarks.data.MapObject;
@@ -33,7 +34,6 @@ import app.organicmaps.sdk.display.DisplayChangedListener;
import app.organicmaps.sdk.display.DisplayManager; import app.organicmaps.sdk.display.DisplayManager;
import app.organicmaps.sdk.display.DisplayType; import app.organicmaps.sdk.display.DisplayType;
import app.organicmaps.sdk.location.LocationState; import app.organicmaps.sdk.location.LocationState;
import app.organicmaps.sdk.routing.RoutingController;
import app.organicmaps.sdk.util.Config; import app.organicmaps.sdk.util.Config;
import app.organicmaps.sdk.util.LocationUtils; import app.organicmaps.sdk.util.LocationUtils;
import app.organicmaps.sdk.util.log.Logger; import app.organicmaps.sdk.util.log.Logger;

View File

@@ -27,10 +27,7 @@ public class SurfaceRenderer implements DefaultLifecycleObserver, SurfaceCallbac
{ {
private static final String TAG = SurfaceRenderer.class.getSimpleName(); private static final String TAG = SurfaceRenderer.class.getSimpleName();
@NonNull
private final CarContext mCarContext; private final CarContext mCarContext;
@NonNull
private final Map mMap = new Map(Car); private final Map mMap = new Map(Car);
@NonNull @NonNull
@@ -59,7 +56,6 @@ public class SurfaceRenderer implements DefaultLifecycleObserver, SurfaceCallbac
mSurface.release(); mSurface.release();
mSurface = surfaceContainer.getSurface(); mSurface = surfaceContainer.getSurface();
mMap.setLocationHelper(MwmApplication.from(mCarContext).getLocationHelper());
mMap.onSurfaceCreated(mCarContext, mSurface, mMap.onSurfaceCreated(mCarContext, mSurface,
new Rect(0, 0, surfaceContainer.getWidth(), surfaceContainer.getHeight()), new Rect(0, 0, surfaceContainer.getWidth(), surfaceContainer.getHeight()),
surfaceContainer.getDpi()); surfaceContainer.getDpi());

View File

@@ -28,11 +28,11 @@ import app.organicmaps.car.util.RoutingUtils;
import app.organicmaps.car.util.ThemeUtils; import app.organicmaps.car.util.ThemeUtils;
import app.organicmaps.car.util.UiHelpers; import app.organicmaps.car.util.UiHelpers;
import app.organicmaps.routing.NavigationService; import app.organicmaps.routing.NavigationService;
import app.organicmaps.routing.RoutingController;
import app.organicmaps.sdk.Framework; import app.organicmaps.sdk.Framework;
import app.organicmaps.sdk.location.LocationHelper; import app.organicmaps.sdk.location.LocationHelper;
import app.organicmaps.sdk.location.LocationListener; import app.organicmaps.sdk.location.LocationListener;
import app.organicmaps.sdk.routing.JunctionInfo; import app.organicmaps.sdk.routing.JunctionInfo;
import app.organicmaps.sdk.routing.RoutingController;
import app.organicmaps.sdk.routing.RoutingInfo; import app.organicmaps.sdk.routing.RoutingInfo;
import app.organicmaps.sdk.sound.TtsPlayer; import app.organicmaps.sdk.sound.TtsPlayer;
import app.organicmaps.sdk.util.LocationUtils; import app.organicmaps.sdk.util.LocationUtils;
@@ -73,7 +73,8 @@ public class NavigationScreen extends BaseMapScreen implements RoutingController
public Template onGetTemplate() public Template onGetTemplate()
{ {
final NavigationTemplate.Builder builder = new NavigationTemplate.Builder(); final NavigationTemplate.Builder builder = new NavigationTemplate.Builder();
builder.setBackgroundColor(Colors.NAVIGATION_TEMPLATE_BACKGROUND); builder.setBackgroundColor(ThemeUtils.isNightMode(getCarContext()) ? Colors.NAVIGATION_TEMPLATE_BACKGROUND_NIGHT
: Colors.NAVIGATION_TEMPLATE_BACKGROUND_DAY);
builder.setActionStrip(createActionStrip()); builder.setActionStrip(createActionStrip());
builder.setMapActionStrip(UiHelpers.createMapActionStrip(getCarContext(), getSurfaceRenderer())); builder.setMapActionStrip(UiHelpers.createMapActionStrip(getCarContext(), getSurfaceRenderer()));

View File

@@ -35,11 +35,11 @@ import app.organicmaps.car.util.OnBackPressedCallback;
import app.organicmaps.car.util.RoutingHelpers; import app.organicmaps.car.util.RoutingHelpers;
import app.organicmaps.car.util.UiHelpers; import app.organicmaps.car.util.UiHelpers;
import app.organicmaps.routing.ResultCodesHelper; import app.organicmaps.routing.ResultCodesHelper;
import app.organicmaps.routing.RoutingController;
import app.organicmaps.sdk.Framework; import app.organicmaps.sdk.Framework;
import app.organicmaps.sdk.Router; import app.organicmaps.sdk.Router;
import app.organicmaps.sdk.bookmarks.data.MapObject; import app.organicmaps.sdk.bookmarks.data.MapObject;
import app.organicmaps.sdk.bookmarks.data.Metadata; import app.organicmaps.sdk.bookmarks.data.Metadata;
import app.organicmaps.sdk.routing.RoutingController;
import app.organicmaps.sdk.routing.RoutingInfo; import app.organicmaps.sdk.routing.RoutingInfo;
import app.organicmaps.sdk.util.Config; import app.organicmaps.sdk.util.Config;
import java.util.Objects; import java.util.Objects;

View File

@@ -12,7 +12,6 @@ import androidx.lifecycle.LifecycleOwner;
import app.organicmaps.R; import app.organicmaps.R;
import app.organicmaps.car.screens.ErrorScreen; import app.organicmaps.car.screens.ErrorScreen;
import app.organicmaps.car.screens.base.BaseScreen; import app.organicmaps.car.screens.base.BaseScreen;
import app.organicmaps.downloader.MapManagerHelper;
import app.organicmaps.sdk.downloader.CountryItem; import app.organicmaps.sdk.downloader.CountryItem;
import app.organicmaps.sdk.downloader.MapManager; import app.organicmaps.sdk.downloader.MapManager;
import app.organicmaps.sdk.util.StringUtils; import app.organicmaps.sdk.util.StringUtils;
@@ -170,7 +169,7 @@ class DownloaderScreen extends BaseScreen
mIsDownloadFailed = true; mIsDownloadFailed = true;
final ErrorScreen.Builder builder = new ErrorScreen.Builder(getCarContext()) final ErrorScreen.Builder builder = new ErrorScreen.Builder(getCarContext())
.setTitle(R.string.country_status_download_failed) .setTitle(R.string.country_status_download_failed)
.setErrorMessage(MapManagerHelper.getErrorCodeStrRes(data.errorCode)) .setErrorMessage(MapManager.getErrorCodeStrRes(data.errorCode))
.setPositiveButton(R.string.downloader_retry, null); .setPositiveButton(R.string.downloader_retry, null);
if (!mIsCancelActionDisabled) if (!mIsCancelActionDisabled)
builder.setNegativeButton(R.string.cancel, this::finish); builder.setNegativeButton(R.string.cancel, this::finish);

View File

@@ -62,29 +62,29 @@ public final class SearchUiHelpers
CarColor color = Colors.DEFAULT; CarColor color = Colors.DEFAULT;
switch (searchResult.description.openNow) switch (searchResult.description.openNow)
{ {
case SearchResult.OPEN_NOW_YES: case SearchResult.OPEN_NOW_YES:
if (searchResult.description.minutesUntilClosed < 60) // less than 1 hour if (searchResult.description.minutesUntilClosed < 60) // less than 1 hour
{ {
final String time = searchResult.description.minutesUntilClosed + " " + carContext.getString(R.string.minute); final String time = searchResult.description.minutesUntilClosed + " " + carContext.getString(R.string.minute);
text = carContext.getString(R.string.closes_in, time); text = carContext.getString(R.string.closes_in, time);
color = Colors.OPENING_HOURS_CLOSES_SOON; color = Colors.OPENING_HOURS_CLOSES_SOON;
} }
else else
{ {
text = carContext.getString(R.string.editor_time_open); text = carContext.getString(R.string.editor_time_open);
color = Colors.OPENING_HOURS_OPEN; color = Colors.OPENING_HOURS_OPEN;
} }
break; break;
case SearchResult.OPEN_NOW_NO: case SearchResult.OPEN_NOW_NO:
if (searchResult.description.minutesUntilOpen < 60) // less than 1 hour if (searchResult.description.minutesUntilOpen < 60) // less than 1 hour
{ {
final String time = searchResult.description.minutesUntilOpen + " " + carContext.getString(R.string.minute); final String time = searchResult.description.minutesUntilOpen + " " + carContext.getString(R.string.minute);
text = carContext.getString(R.string.opens_in, time); text = carContext.getString(R.string.opens_in, time);
} }
else else
text = carContext.getString(R.string.closed); text = carContext.getString(R.string.closed);
color = Colors.OPENING_HOURS_CLOSED; color = Colors.OPENING_HOURS_CLOSED;
break; break;
} }
result.append(text); result.append(text);

View File

@@ -11,7 +11,8 @@ public final class Colors
public static final CarColor OPENING_HOURS_CLOSES_SOON = CarColor.YELLOW; public static final CarColor OPENING_HOURS_CLOSES_SOON = CarColor.YELLOW;
public static final CarColor OPENING_HOURS_CLOSED = CarColor.RED; public static final CarColor OPENING_HOURS_CLOSED = CarColor.RED;
public static final CarColor START_NAVIGATION = CarColor.GREEN; public static final CarColor START_NAVIGATION = CarColor.GREEN;
public static final CarColor NAVIGATION_TEMPLATE_BACKGROUND = CarColor.GREEN; public static final CarColor NAVIGATION_TEMPLATE_BACKGROUND_DAY = CarColor.GREEN;
public static final CarColor NAVIGATION_TEMPLATE_BACKGROUND_NIGHT = CarColor.DEFAULT;
public static final CarColor BUTTON_ACCEPT = CarColor.GREEN; public static final CarColor BUTTON_ACCEPT = CarColor.GREEN;
private Colors() {} private Colors() {}

View File

@@ -7,9 +7,9 @@ import androidx.car.app.CarContext;
import androidx.car.app.ScreenManager; import androidx.car.app.ScreenManager;
import app.organicmaps.car.screens.download.DownloadMapsScreen; import app.organicmaps.car.screens.download.DownloadMapsScreen;
import app.organicmaps.car.screens.download.DownloadMapsScreenBuilder; import app.organicmaps.car.screens.download.DownloadMapsScreenBuilder;
import app.organicmaps.routing.RoutingController;
import app.organicmaps.sdk.downloader.CountryItem; import app.organicmaps.sdk.downloader.CountryItem;
import app.organicmaps.sdk.downloader.MapManager; import app.organicmaps.sdk.downloader.MapManager;
import app.organicmaps.sdk.routing.RoutingController;
public class CurrentCountryChangedListener implements MapManager.CurrentCountryChangedListener public class CurrentCountryChangedListener implements MapManager.CurrentCountryChangedListener
{ {

View File

@@ -15,13 +15,13 @@ import app.organicmaps.car.CarAppService;
import app.organicmaps.car.SurfaceRenderer; import app.organicmaps.car.SurfaceRenderer;
import app.organicmaps.car.screens.NavigationScreen; import app.organicmaps.car.screens.NavigationScreen;
import app.organicmaps.car.screens.search.SearchScreen; import app.organicmaps.car.screens.search.SearchScreen;
import app.organicmaps.routing.RoutingController;
import app.organicmaps.sdk.Framework; import app.organicmaps.sdk.Framework;
import app.organicmaps.sdk.Map; import app.organicmaps.sdk.Map;
import app.organicmaps.sdk.api.ParsedSearchRequest; import app.organicmaps.sdk.api.ParsedSearchRequest;
import app.organicmaps.sdk.api.RequestType; import app.organicmaps.sdk.api.RequestType;
import app.organicmaps.sdk.display.DisplayManager; import app.organicmaps.sdk.display.DisplayManager;
import app.organicmaps.sdk.display.DisplayType; import app.organicmaps.sdk.display.DisplayType;
import app.organicmaps.sdk.routing.RoutingController;
import app.organicmaps.sdk.util.log.Logger; import app.organicmaps.sdk.util.log.Logger;
public final class IntentUtils public final class IntentUtils
@@ -65,36 +65,36 @@ public final class IntentUtils
final ScreenManager screenManager = carContext.getCarService(ScreenManager.class); final ScreenManager screenManager = carContext.getCarService(ScreenManager.class);
switch (Framework.nativeParseAndSetApiUrl(uri.toString())) switch (Framework.nativeParseAndSetApiUrl(uri.toString()))
{ {
case RequestType.INCORRECT: return; case RequestType.INCORRECT: return;
case RequestType.MAP: case RequestType.MAP:
screenManager.popToRoot(); screenManager.popToRoot();
Map.executeMapApiRequest(); Map.executeMapApiRequest();
return; return;
case RequestType.SEARCH: case RequestType.SEARCH:
screenManager.popToRoot(); screenManager.popToRoot();
final ParsedSearchRequest request = Framework.nativeGetParsedSearchRequest(); final ParsedSearchRequest request = Framework.nativeGetParsedSearchRequest();
final double[] latlon = Framework.nativeGetParsedCenterLatLon(); final double[] latlon = Framework.nativeGetParsedCenterLatLon();
if (latlon != null) if (latlon != null)
{ {
Framework.nativeStopLocationFollow(); Framework.nativeStopLocationFollow();
Framework.nativeSetViewportCenter(latlon[0], latlon[1], SEARCH_IN_VIEWPORT_ZOOM); Framework.nativeSetViewportCenter(latlon[0], latlon[1], SEARCH_IN_VIEWPORT_ZOOM);
// We need to update viewport for search api manually because of drape engine // We need to update viewport for search api manually because of drape engine
// will not notify subscribers when search activity is shown. // will not notify subscribers when search activity is shown.
if (!request.mIsSearchOnMap) if (!request.mIsSearchOnMap)
Framework.nativeSetSearchViewport(latlon[0], latlon[1], SEARCH_IN_VIEWPORT_ZOOM); Framework.nativeSetSearchViewport(latlon[0], latlon[1], SEARCH_IN_VIEWPORT_ZOOM);
} }
final SearchScreen.Builder builder = new SearchScreen.Builder(carContext, surfaceRenderer); final SearchScreen.Builder builder = new SearchScreen.Builder(carContext, surfaceRenderer);
builder.setQuery(request.mQuery); builder.setQuery(request.mQuery);
if (request.mLocale != null) if (request.mLocale != null)
builder.setLocale(request.mLocale); builder.setLocale(request.mLocale);
screenManager.popToRoot(); screenManager.popToRoot();
screenManager.push(builder.build()); screenManager.push(builder.build());
return; return;
case RequestType.ROUTE: Logger.e(TAG, "Route API is not supported by Android Auto: " + uri); return; case RequestType.ROUTE: Logger.e(TAG, "Route API is not supported by Android Auto: " + uri); return;
case RequestType.CROSSHAIR: Logger.e(TAG, "Crosshair API is not supported by Android Auto: " + uri); return; case RequestType.CROSSHAIR: Logger.e(TAG, "Crosshair API is not supported by Android Auto: " + uri); return;
case RequestType.MENU: Logger.e(TAG, "Menu API is not supported by Android Auto: " + uri); return; case RequestType.MENU: Logger.e(TAG, "Menu API is not supported by Android Auto: " + uri); return;
case RequestType.SETTINGS: Logger.e(TAG, "Settings API is not supported by Android Auto: " + uri); case RequestType.SETTINGS: Logger.e(TAG, "Settings API is not supported by Android Auto: " + uri);
} }
} }

View File

@@ -8,22 +8,21 @@ import androidx.annotation.StringRes;
import androidx.annotation.UiThread; import androidx.annotation.UiThread;
import androidx.car.app.CarContext; import androidx.car.app.CarContext;
import app.organicmaps.R; import app.organicmaps.R;
import app.organicmaps.routing.RoutingController;
import app.organicmaps.sdk.MapStyle; import app.organicmaps.sdk.MapStyle;
import app.organicmaps.sdk.routing.RoutingController;
import app.organicmaps.sdk.util.Config;
public final class ThemeUtils public final class ThemeUtils
{ {
public enum ThemeMode public enum ThemeMode
{ {
AUTO(R.string.auto, Config.UiTheme.AUTO), AUTO(R.string.auto, R.string.theme_auto),
LIGHT(R.string.off, Config.UiTheme.DEFAULT), LIGHT(R.string.off, R.string.theme_default),
NIGHT(R.string.on, Config.UiTheme.NIGHT); NIGHT(R.string.on, R.string.theme_night);
ThemeMode(@StringRes int titleId, @NonNull String config) ThemeMode(@StringRes int titleId, @StringRes int prefsKeyId)
{ {
mTitleId = titleId; mTitleId = titleId;
mConfig = config; mPrefsKeyId = prefsKeyId;
} }
@StringRes @StringRes
@@ -32,16 +31,16 @@ public final class ThemeUtils
return mTitleId; return mTitleId;
} }
@NonNull @StringRes
public String getConfig() public int getPrefsKeyId()
{ {
return mConfig; return mPrefsKeyId;
} }
@StringRes @StringRes
private final int mTitleId; private final int mTitleId;
@NonNull @StringRes
private final String mConfig; private final int mPrefsKeyId;
} }
private static final String ANDROID_AUTO_PREFERENCES_FILE_KEY = "ANDROID_AUTO_PREFERENCES_FILE_KEY"; private static final String ANDROID_AUTO_PREFERENCES_FILE_KEY = "ANDROID_AUTO_PREFERENCES_FILE_KEY";
@@ -80,20 +79,23 @@ public final class ThemeUtils
@UiThread @UiThread
public static void setThemeMode(@NonNull CarContext context, @NonNull ThemeMode themeMode) public static void setThemeMode(@NonNull CarContext context, @NonNull ThemeMode themeMode)
{ {
getSharedPreferences(context).edit().putString(THEME_KEY, themeMode.getConfig()).commit(); getSharedPreferences(context).edit().putString(THEME_KEY, context.getString(themeMode.getPrefsKeyId())).commit();
update(context, themeMode); update(context, themeMode);
} }
@NonNull @NonNull
public static ThemeMode getThemeMode(@NonNull CarContext context) public static ThemeMode getThemeMode(@NonNull CarContext context)
{ {
final String themeMode = getSharedPreferences(context).getString(THEME_KEY, ThemeMode.AUTO.getConfig()); final String autoTheme = context.getString(R.string.theme_auto);
final String lightTheme = context.getString(R.string.theme_default);
final String nightTheme = context.getString(R.string.theme_night);
final String themeMode = getSharedPreferences(context).getString(THEME_KEY, autoTheme);
if (themeMode.equals(ThemeMode.AUTO.getConfig())) if (themeMode.equals(autoTheme))
return ThemeMode.AUTO; return ThemeMode.AUTO;
else if (themeMode.equals(ThemeMode.LIGHT.getConfig())) else if (themeMode.equals(lightTheme))
return ThemeMode.LIGHT; return ThemeMode.LIGHT;
else if (themeMode.equals(ThemeMode.NIGHT.getConfig())) else if (themeMode.equals(nightTheme))
return ThemeMode.NIGHT; return ThemeMode.NIGHT;
else else
throw new IllegalArgumentException("Unsupported value"); throw new IllegalArgumentException("Unsupported value");

View File

@@ -171,20 +171,20 @@ public final class UiHelpers
int drawableRes; int drawableRes;
switch (locationMode) switch (locationMode)
{ {
case LocationState.PENDING_POSITION, LocationState.NOT_FOLLOW_NO_POSITION -> case LocationState.PENDING_POSITION, LocationState.NOT_FOLLOW_NO_POSITION ->
drawableRes = R.drawable.ic_location_off; drawableRes = R.drawable.ic_location_off;
case LocationState.NOT_FOLLOW -> drawableRes = R.drawable.ic_not_follow; case LocationState.NOT_FOLLOW -> drawableRes = R.drawable.ic_not_follow;
case LocationState.FOLLOW -> case LocationState.FOLLOW ->
{ {
drawableRes = R.drawable.ic_follow; drawableRes = R.drawable.ic_follow;
tintColor = Colors.LOCATION_TINT; tintColor = Colors.LOCATION_TINT;
} }
case LocationState.FOLLOW_AND_ROTATE -> case LocationState.FOLLOW_AND_ROTATE ->
{ {
drawableRes = R.drawable.ic_follow_and_rotate; drawableRes = R.drawable.ic_follow_and_rotate;
tintColor = Colors.LOCATION_TINT; tintColor = Colors.LOCATION_TINT;
} }
default -> throw new IllegalArgumentException("Invalid button mode: " + locationMode); default -> throw new IllegalArgumentException("Invalid button mode: " + locationMode);
} }
final CarIcon icon = final CarIcon icon =

View File

@@ -15,7 +15,7 @@ import app.organicmaps.sdk.downloader.CountryItem;
import app.organicmaps.sdk.downloader.MapManager; import app.organicmaps.sdk.downloader.MapManager;
import app.organicmaps.sdk.downloader.UpdateInfo; import app.organicmaps.sdk.downloader.UpdateInfo;
import app.organicmaps.sdk.util.StringUtils; import app.organicmaps.sdk.util.StringUtils;
import app.organicmaps.util.UiUtils; import app.organicmaps.sdk.util.UiUtils;
import com.google.android.material.button.MaterialButton; import com.google.android.material.button.MaterialButton;
import com.google.android.material.floatingactionbutton.FloatingActionButton; import com.google.android.material.floatingactionbutton.FloatingActionButton;
@@ -29,7 +29,7 @@ class BottomPanel
@Override @Override
public void onClick(View v) public void onClick(View v)
{ {
MapManagerHelper.warn3gAndDownload(mFragment.requireActivity(), mFragment.getCurrentRoot(), null); MapManager.warn3gAndDownload(mFragment.requireActivity(), mFragment.getCurrentRoot(), null);
} }
}; };
@@ -38,10 +38,7 @@ class BottomPanel
public void onClick(View v) public void onClick(View v)
{ {
final String country = mFragment.getCurrentRoot(); final String country = mFragment.getCurrentRoot();
MapManagerHelper.warnOn3gUpdate(mFragment.requireActivity(), country, () -> { MapManager.warnOn3gUpdate(mFragment.requireActivity(), country, () -> MapManager.startUpdate(country));
DownloaderService.startForegroundService();
MapManagerHelper.startUpdate(country);
});
} }
}; };
@@ -58,7 +55,7 @@ class BottomPanel
@Override @Override
public void onClick(View v) public void onClick(View v)
{ {
MapManagerHelper.warn3gAndRetry(mFragment.requireActivity(), mFragment.getCurrentRoot(), null); MapManager.warn3gAndRetry(mFragment.requireActivity(), mFragment.getCurrentRoot(), null);
} }
}; };
@@ -118,15 +115,15 @@ class BottomPanel
{ {
switch (status) switch (status)
{ {
case STATUS_UPDATABLE -> case STATUS_UPDATABLE ->
{ {
UpdateInfo info = MapManager.nativeGetUpdateInfo(root); UpdateInfo info = MapManager.nativeGetUpdateInfo(root);
setUpdateAllState(info); setUpdateAllState(info);
} // Special case for "Countries" node when no maps currently downloaded. } // Special case for "Countries" node when no maps currently downloaded.
case STATUS_DOWNLOADABLE, STATUS_DONE, STATUS_PARTLY -> show = false; case STATUS_DOWNLOADABLE, STATUS_DONE, STATUS_PARTLY -> show = false;
case STATUS_PROGRESS, STATUS_APPLYING, STATUS_ENQUEUED -> setCancelState(); case STATUS_PROGRESS, STATUS_APPLYING, STATUS_ENQUEUED -> setCancelState();
case STATUS_FAILED -> setRetryFailedStates(); case STATUS_FAILED -> setRetryFailedStates();
default -> throw new IllegalArgumentException("Inappropriate status for \"" + root + "\": " + status); default -> throw new IllegalArgumentException("Inappropriate status for \"" + root + "\": " + status);
} }
} }
else else
@@ -136,15 +133,15 @@ class BottomPanel
{ {
switch (status) switch (status)
{ {
case STATUS_UPDATABLE -> case STATUS_UPDATABLE ->
{ {
UpdateInfo info = MapManager.nativeGetUpdateInfo(root); UpdateInfo info = MapManager.nativeGetUpdateInfo(root);
setUpdateAllState(info); setUpdateAllState(info);
} }
case STATUS_DONE -> show = false; case STATUS_DONE -> show = false;
case STATUS_PROGRESS, STATUS_APPLYING, STATUS_ENQUEUED -> setCancelState(); case STATUS_PROGRESS, STATUS_APPLYING, STATUS_ENQUEUED -> setCancelState();
case STATUS_FAILED -> setRetryFailedStates(); case STATUS_FAILED -> setRetryFailedStates();
default -> setDownloadAllState(); default -> setDownloadAllState();
} }
} }
} }

View File

@@ -16,7 +16,7 @@ import app.organicmaps.base.BaseMwmFragmentActivity;
import app.organicmaps.sdk.downloader.CountryItem; import app.organicmaps.sdk.downloader.CountryItem;
import app.organicmaps.sdk.downloader.MapManager; import app.organicmaps.sdk.downloader.MapManager;
import app.organicmaps.sdk.util.StringUtils; import app.organicmaps.sdk.util.StringUtils;
import app.organicmaps.util.UiUtils; import app.organicmaps.sdk.util.UiUtils;
import app.organicmaps.widget.WheelProgressView; import app.organicmaps.widget.WheelProgressView;
import com.google.android.material.button.MaterialButton; import com.google.android.material.button.MaterialButton;
import com.google.android.material.textview.MaterialTextView; import com.google.android.material.textview.MaterialTextView;
@@ -70,9 +70,9 @@ public class CountrySuggestFragment extends BaseMwmFragment implements View.OnCl
switch (item.newStatus) switch (item.newStatus)
{ {
case CountryItem.STATUS_FAILED: updateViews(); return; case CountryItem.STATUS_FAILED: updateViews(); return;
case CountryItem.STATUS_DONE: exitFragment(); return; case CountryItem.STATUS_DONE: exitFragment(); return;
} }
break; break;
@@ -198,8 +198,7 @@ public class CountrySuggestFragment extends BaseMwmFragment implements View.OnCl
final int id = v.getId(); final int id = v.getId();
if (id == R.id.btn__download_map) if (id == R.id.btn__download_map)
{ {
MapManagerHelper.warn3gAndDownload(requireActivity(), mCurrentCountry.id, MapManager.warn3gAndDownload(requireActivity(), mCurrentCountry.id, () -> mDownloadingCountry = mCurrentCountry);
() -> mDownloadingCountry = mCurrentCountry);
} }
else if (id == R.id.btn__select_map) else if (id == R.id.btn__select_map)
{ {

View File

@@ -20,11 +20,11 @@ import androidx.recyclerview.widget.RecyclerView;
import app.organicmaps.MwmActivity; import app.organicmaps.MwmActivity;
import app.organicmaps.MwmApplication; import app.organicmaps.MwmApplication;
import app.organicmaps.R; import app.organicmaps.R;
import app.organicmaps.routing.RoutingController;
import app.organicmaps.sdk.downloader.CountryItem; import app.organicmaps.sdk.downloader.CountryItem;
import app.organicmaps.sdk.downloader.MapManager; import app.organicmaps.sdk.downloader.MapManager;
import app.organicmaps.sdk.routing.RoutingController;
import app.organicmaps.sdk.util.StringUtils; import app.organicmaps.sdk.util.StringUtils;
import app.organicmaps.util.UiUtils; import app.organicmaps.sdk.util.UiUtils;
import app.organicmaps.util.bottomsheet.MenuBottomSheetFragment; import app.organicmaps.util.bottomsheet.MenuBottomSheetFragment;
import app.organicmaps.util.bottomsheet.MenuBottomSheetItem; import app.organicmaps.util.bottomsheet.MenuBottomSheetItem;
import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.dialog.MaterialAlertDialogBuilder;
@@ -93,7 +93,7 @@ class DownloaderAdapter extends RecyclerView.Adapter<DownloaderAdapter.ViewHolde
private void onDownloadActionSelected(final CountryItem item, DownloaderAdapter adapter) private void onDownloadActionSelected(final CountryItem item, DownloaderAdapter adapter)
{ {
MapManagerHelper.warn3gAndDownload(adapter.mActivity, item.id, null); MapManager.warn3gAndDownload(adapter.mActivity, item.id, null);
} }
private void onUpdateActionSelected(final CountryItem item, DownloaderAdapter adapter) private void onUpdateActionSelected(final CountryItem item, DownloaderAdapter adapter)
@@ -101,7 +101,7 @@ class DownloaderAdapter extends RecyclerView.Adapter<DownloaderAdapter.ViewHolde
item.update(); item.update();
if (item.status != CountryItem.STATUS_UPDATABLE) if (item.status != CountryItem.STATUS_UPDATABLE)
return; return;
MapManagerHelper.warnOn3gUpdate(adapter.mActivity, item.id, () -> MapManagerHelper.startUpdate(item.id)); MapManager.warnOn3gUpdate(adapter.mActivity, item.id, () -> MapManager.startUpdate(item.id));
} }
private void onExploreActionSelected(CountryItem item, DownloaderAdapter adapter) private void onExploreActionSelected(CountryItem item, DownloaderAdapter adapter)
@@ -206,7 +206,7 @@ class DownloaderAdapter extends RecyclerView.Adapter<DownloaderAdapter.ViewHolde
{ {
if (item.isLeafNode && item.newStatus == CountryItem.STATUS_FAILED) if (item.isLeafNode && item.newStatus == CountryItem.STATUS_FAILED)
{ {
MapManagerHelper.showError(mActivity, item, null); MapManager.showError(mActivity, item, null);
break; break;
} }
} }
@@ -287,41 +287,41 @@ class DownloaderAdapter extends RecyclerView.Adapter<DownloaderAdapter.ViewHolde
ArrayList<MenuBottomSheetItem> items = new ArrayList<>(); ArrayList<MenuBottomSheetItem> items = new ArrayList<>();
switch (mSelectedItem.status) switch (mSelectedItem.status)
{ {
case CountryItem.STATUS_DOWNLOADABLE: items.add(getDownloadMenuItem()); break; case CountryItem.STATUS_DOWNLOADABLE: items.add(getDownloadMenuItem()); break;
case CountryItem.STATUS_UPDATABLE: case CountryItem.STATUS_UPDATABLE:
items.add(getUpdateMenuItem()); items.add(getUpdateMenuItem());
// Fallthrough // Fallthrough
case CountryItem.STATUS_DONE: case CountryItem.STATUS_DONE:
if (!mSelectedItem.isExpandable()) if (!mSelectedItem.isExpandable())
items.add(getExploreMenuItem()); items.add(getExploreMenuItem());
appendDeleteMenuItem(items);
break;
case CountryItem.STATUS_FAILED:
items.add(getCancelMenuItem());
if (mSelectedItem.present)
{
appendDeleteMenuItem(items); appendDeleteMenuItem(items);
items.add(getExploreMenuItem()); break;
}
break;
case CountryItem.STATUS_PROGRESS: case CountryItem.STATUS_FAILED:
case CountryItem.STATUS_APPLYING: items.add(getCancelMenuItem());
case CountryItem.STATUS_ENQUEUED:
items.add(getCancelMenuItem());
if (mSelectedItem.present) if (mSelectedItem.present)
items.add(getExploreMenuItem()); {
break; appendDeleteMenuItem(items);
items.add(getExploreMenuItem());
}
break;
case CountryItem.STATUS_PARTLY: case CountryItem.STATUS_PROGRESS:
items.add(getDownloadMenuItem()); case CountryItem.STATUS_APPLYING:
appendDeleteMenuItem(items); case CountryItem.STATUS_ENQUEUED:
break; items.add(getCancelMenuItem());
if (mSelectedItem.present)
items.add(getExploreMenuItem());
break;
case CountryItem.STATUS_PARTLY:
items.add(getDownloadMenuItem());
appendDeleteMenuItem(items);
break;
} }
return items; return items;
} }
@@ -372,23 +372,20 @@ class DownloaderAdapter extends RecyclerView.Adapter<DownloaderAdapter.ViewHolde
{ {
switch (mItem.status) switch (mItem.status)
{ {
case CountryItem.STATUS_DONE, CountryItem.STATUS_PROGRESS, CountryItem.STATUS_APPLYING, case CountryItem.STATUS_DONE, CountryItem.STATUS_PROGRESS, CountryItem.STATUS_APPLYING,
CountryItem.STATUS_ENQUEUED -> CountryItem.STATUS_ENQUEUED ->
processLongClick();
case CountryItem.STATUS_DOWNLOADABLE, CountryItem.STATUS_PARTLY ->
{
if (clickOnStatus)
onDownloadActionSelected(mItem, DownloaderAdapter.this);
else
processLongClick(); processLongClick();
} case CountryItem.STATUS_DOWNLOADABLE, CountryItem.STATUS_PARTLY ->
case CountryItem.STATUS_FAILED -> {
{ if (clickOnStatus)
MapManagerHelper.warn3gAndRetry(mActivity, mItem.id, null); onDownloadActionSelected(mItem, DownloaderAdapter.this);
} else
case CountryItem.STATUS_UPDATABLE -> processLongClick();
MapManagerHelper.warnOn3gUpdate(mActivity, mItem.id, () -> MapManagerHelper.startUpdate(mItem.id)); }
default -> throw new IllegalArgumentException("Inappropriate item status: " + mItem.status); case CountryItem.STATUS_FAILED -> MapManager.warn3gAndRetry(mActivity, mItem.id, null);
case CountryItem.STATUS_UPDATABLE ->
MapManager.warnOn3gUpdate(mActivity, mItem.id, () -> MapManager.startUpdate(mItem.id));
default -> throw new IllegalArgumentException("Inappropriate item status: " + mItem.status);
} }
} }
@@ -538,33 +535,33 @@ class DownloaderAdapter extends RecyclerView.Adapter<DownloaderAdapter.ViewHolde
{ {
switch (ci.category) switch (ci.category)
{ {
case CountryItem.CATEGORY_NEAR_ME -> case CountryItem.CATEGORY_NEAR_ME ->
{
if (ci.category != prev)
{ {
headerId = CountryItem.CATEGORY_NEAR_ME; if (ci.category != prev)
mItemsAndHeader.add(new GenericItem(mActivity.getString(R.string.downloader_near_me_subtitle))); {
headerId = CountryItem.CATEGORY_NEAR_ME;
mItemsAndHeader.add(new GenericItem(mActivity.getString(R.string.downloader_near_me_subtitle)));
prev = ci.category;
}
}
case CountryItem.CATEGORY_DOWNLOADED ->
{
if (ci.category != prev)
{
headerId = CountryItem.CATEGORY_DOWNLOADED;
mItemsAndHeader.add(new GenericItem(mActivity.getString(R.string.downloader_downloaded_subtitle)));
prev = ci.category;
}
}
default ->
{
int prevHeader = headerId;
headerId = CountryItem.CATEGORY_AVAILABLE + ci.name.charAt(0);
if (headerId != prevHeader)
mItemsAndHeader.add(new GenericItem(StringUtils.toUpperCase(ci.name.substring(0, 1))));
prev = ci.category; prev = ci.category;
} }
} }
case CountryItem.CATEGORY_DOWNLOADED ->
{
if (ci.category != prev)
{
headerId = CountryItem.CATEGORY_DOWNLOADED;
mItemsAndHeader.add(new GenericItem(mActivity.getString(R.string.downloader_downloaded_subtitle)));
prev = ci.category;
}
}
default ->
{
int prevHeader = headerId;
headerId = CountryItem.CATEGORY_AVAILABLE + ci.name.charAt(0);
if (headerId != prevHeader)
mItemsAndHeader.add(new GenericItem(StringUtils.toUpperCase(ci.name.substring(0, 1))));
prev = ci.category;
}
}
ci.headerId = headerId; ci.headerId = headerId;
} }
mItemsAndHeader.add(new GenericItem(ci)); mItemsAndHeader.add(new GenericItem(ci));

View File

@@ -24,8 +24,8 @@ import app.organicmaps.widget.PlaceholderView;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
public class DownloaderFragment public class DownloaderFragment extends BaseMwmRecyclerFragment<DownloaderAdapter>
extends BaseMwmRecyclerFragment<DownloaderAdapter> implements MenuBottomSheetFragment.MenuBottomSheetInterface implements MenuBottomSheetFragment.MenuBottomSheetInterface
{ {
private DownloaderToolbarController mToolbarController; private DownloaderToolbarController mToolbarController;
@@ -153,8 +153,7 @@ public class DownloaderFragment
mBottomPanel = new BottomPanel(this, view); mBottomPanel = new BottomPanel(this, view);
mToolbarController = new DownloaderToolbarController(view, requireActivity(), this); mToolbarController = new DownloaderToolbarController(view, requireActivity(), this);
requireActivity().getOnBackPressedDispatcher().addCallback(getViewLifecycleOwner(), requireActivity().getOnBackPressedDispatcher().addCallback(getViewLifecycleOwner(), mToolbarController.getBackPressedCallback());
mToolbarController.getBackPressedCallback());
update(); update();
} }
@@ -196,8 +195,7 @@ public class DownloaderFragment
{ {
if (mAdapter == null) if (mAdapter == null)
mAdapter = new DownloaderAdapter(this); mAdapter = new DownloaderAdapter(this);
requireActivity().getOnBackPressedDispatcher().addCallback(getViewLifecycleOwner(), requireActivity().getOnBackPressedDispatcher().addCallback(getViewLifecycleOwner(), mAdapter.getBackPressedCallback());
mAdapter.getBackPressedCallback());
return mAdapter; return mAdapter;
} }

View File

@@ -9,8 +9,7 @@ import androidx.core.view.OnApplyWindowInsetsListener;
import androidx.core.view.WindowInsetsCompat; import androidx.core.view.WindowInsetsCompat;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import app.organicmaps.R; import app.organicmaps.R;
import app.organicmaps.sdk.util.Utils; import app.organicmaps.sdk.util.UiUtils;
import app.organicmaps.util.UiUtils;
import app.organicmaps.util.WindowInsetUtils; import app.organicmaps.util.WindowInsetUtils;
final class DownloaderInsetsListener implements OnApplyWindowInsetsListener final class DownloaderInsetsListener implements OnApplyWindowInsetsListener
@@ -56,7 +55,7 @@ final class DownloaderInsetsListener implements OnApplyWindowInsetsListener
private void applyInsetsToButtons(Insets insets) private void applyInsetsToButtons(Insets insets)
{ {
int baseMargin = Utils.dimen(mContext, R.dimen.margin_base); int baseMargin = UiUtils.dimen(mContext, R.dimen.margin_base);
ViewGroup.MarginLayoutParams fabParams = (ViewGroup.MarginLayoutParams) mFab.getLayoutParams(); ViewGroup.MarginLayoutParams fabParams = (ViewGroup.MarginLayoutParams) mFab.getLayoutParams();
ViewGroup.MarginLayoutParams buttonParams = (ViewGroup.MarginLayoutParams) mButton.getLayoutParams(); ViewGroup.MarginLayoutParams buttonParams = (ViewGroup.MarginLayoutParams) mButton.getLayoutParams();

View File

@@ -20,7 +20,6 @@ import app.organicmaps.R;
import app.organicmaps.sdk.downloader.MapManager; import app.organicmaps.sdk.downloader.MapManager;
import app.organicmaps.sdk.util.StringUtils; import app.organicmaps.sdk.util.StringUtils;
import app.organicmaps.sdk.util.log.Logger; import app.organicmaps.sdk.util.log.Logger;
import java.util.Objects;
public class DownloaderNotifier public class DownloaderNotifier
{ {
@@ -30,8 +29,7 @@ public class DownloaderNotifier
public static final int NOTIFICATION_ID = 1; public static final int NOTIFICATION_ID = 1;
private final Context mContext; private final Context mContext;
private NotificationCompat.Builder mProgressNotificationBuilder = null; private NotificationCompat.Builder mProgressNotificationBuilder;
private String mNotificationCountryId = null;
public DownloaderNotifier(Context context) public DownloaderNotifier(Context context)
{ {
@@ -47,7 +45,6 @@ public class DownloaderNotifier
.setShowBadge(true) .setShowBadge(true)
.setVibrationEnabled(false) .setVibrationEnabled(false)
.setLightsEnabled(false) .setLightsEnabled(false)
.setSound(null, null)
.build(); .build();
notificationManager.createNotificationChannel(channel); notificationManager.createNotificationChannel(channel);
} }
@@ -65,6 +62,8 @@ public class DownloaderNotifier
final String countryName = MapManager.nativeGetName(countryId); final String countryName = MapManager.nativeGetName(countryId);
final String content = mContext.getString(R.string.download_country_failed, countryName); final String content = mContext.getString(R.string.download_country_failed, countryName);
var contentPendingIntent = getNotificationPendingIntent(countryId);
final Notification notification = new NotificationCompat.Builder(mContext, CHANNEL_ID) final Notification notification = new NotificationCompat.Builder(mContext, CHANNEL_ID)
.setAutoCancel(true) .setAutoCancel(true)
.setCategory(NotificationCompat.CATEGORY_ERROR) .setCategory(NotificationCompat.CATEGORY_ERROR)
@@ -75,7 +74,7 @@ public class DownloaderNotifier
.setContentText(content) .setContentText(content)
.setShowWhen(true) .setShowWhen(true)
.setTicker(getTicker(mContext, title, content)) .setTicker(getTicker(mContext, title, content))
.setContentIntent(getNotificationPendingIntent(countryId)) .setContentIntent(contentPendingIntent)
.setOnlyAlertOnce(true) .setOnlyAlertOnce(true)
.build(); .build();
@@ -111,41 +110,32 @@ public class DownloaderNotifier
@NonNull @NonNull
public Notification buildProgressNotification(@Nullable String countryId, int maxProgress, int progress) public Notification buildProgressNotification(@Nullable String countryId, int maxProgress, int progress)
{ {
var builder = getNotificationBuilder(countryId); var builder = startNotification(countryId);
/// @todo Doesn't work properly .. Bad input sizes?
// builder.setProgress(maxProgress, progress, maxProgress == 0); builder.setProgress(maxProgress, progress, maxProgress == 0);
builder.setProgress(maxProgress, progress, true); builder.setContentText("Download in progress");
return builder.build(); return builder.build();
} }
@NonNull @NonNull
private NotificationCompat.Builder getNotificationBuilder(@Nullable String countryId) private NotificationCompat.Builder startNotification(@Nullable String countryId)
{ {
if (mProgressNotificationBuilder == null || !Objects.equals(countryId, mNotificationCountryId)) final String title = mContext.getString(R.string.app_name);
{
mNotificationCountryId = countryId;
final String countryName = countryId != null ? MapManager.nativeGetName(countryId) : "";
mProgressNotificationBuilder = return new NotificationCompat.Builder(mContext, CHANNEL_ID)
new NotificationCompat.Builder(mContext, CHANNEL_ID) .setAutoCancel(true)
.setAutoCancel(true) .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC) .setSmallIcon(R.drawable.ic_logo_small)
.setSmallIcon(R.drawable.ic_logo_small) .setColor(ContextCompat.getColor(mContext, R.color.notification))
.setColor(ContextCompat.getColor(mContext, R.color.notification)) .setShowWhen(true)
.setShowWhen(true) .setContentTitle(title)
.setContentTitle(mContext.getString(R.string.app_name)) .setContentIntent(getNotificationPendingIntent(countryId));
.setContentIntent(getNotificationPendingIntent(countryId))
.setContentText(mContext.getString(R.string.downloader_downloading) + " " + countryName)
.setSound(null);
}
return mProgressNotificationBuilder;
} }
@NonNull @NonNull
private PendingIntent getNotificationPendingIntent(@Nullable String countryId) private PendingIntent getNotificationPendingIntent(@Nullable String countryId)
{ {
/// @todo Zooming to the countryId when tapping on the notification?
/// Shows very low zoom level, need z=9/10, I suppose ...
final int FLAG_IMMUTABLE = Build.VERSION.SDK_INT < Build.VERSION_CODES.M ? 0 : PendingIntent.FLAG_IMMUTABLE; final int FLAG_IMMUTABLE = Build.VERSION.SDK_INT < Build.VERSION_CODES.M ? 0 : PendingIntent.FLAG_IMMUTABLE;
final Intent contentIntent = MwmActivity.createShowMapIntent(mContext, countryId); final Intent contentIntent = MwmActivity.createShowMapIntent(mContext, countryId);
contentIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); contentIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

View File

@@ -97,7 +97,7 @@ public class DownloaderService extends Service implements MapManager.StorageCall
} }
@Override @Override
public void onProgress(String countryId, long bytesDownloaded, long bytesTotal) public void onProgress(String countryId, long localSize, long remoteSize)
{ {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU
&& ContextCompat.checkSelfPermission(this, POST_NOTIFICATIONS) != PERMISSION_GRANTED) && ContextCompat.checkSelfPermission(this, POST_NOTIFICATIONS) != PERMISSION_GRANTED)
@@ -106,7 +106,8 @@ public class DownloaderService extends Service implements MapManager.StorageCall
return; return;
} }
mNotifier.notifyProgress(countryId, (int) bytesTotal, (int) bytesDownloaded); // TODO: How to calculate progress?
mNotifier.notifyProgress();
} }
@Override @Override

View File

@@ -6,8 +6,8 @@ import androidx.annotation.AttrRes;
import androidx.annotation.DrawableRes; import androidx.annotation.DrawableRes;
import app.organicmaps.R; import app.organicmaps.R;
import app.organicmaps.sdk.downloader.CountryItem; import app.organicmaps.sdk.downloader.CountryItem;
import app.organicmaps.sdk.util.UiUtils;
import app.organicmaps.util.ThemeUtils; import app.organicmaps.util.ThemeUtils;
import app.organicmaps.util.UiUtils;
import app.organicmaps.widget.WheelProgressView; import app.organicmaps.widget.WheelProgressView;
import com.google.android.material.imageview.ShapeableImageView; import com.google.android.material.imageview.ShapeableImageView;

View File

@@ -5,7 +5,7 @@ import android.content.Intent;
import android.text.TextUtils; import android.text.TextUtils;
import android.view.View; import android.view.View;
import app.organicmaps.R; import app.organicmaps.R;
import app.organicmaps.util.UiUtils; import app.organicmaps.sdk.util.UiUtils;
import app.organicmaps.widget.SearchToolbarController; import app.organicmaps.widget.SearchToolbarController;
class DownloaderToolbarController extends SearchToolbarController class DownloaderToolbarController extends SearchToolbarController

View File

@@ -1,223 +0,0 @@
package app.organicmaps.downloader;
import android.app.Activity;
import android.text.TextUtils;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.appcompat.app.AlertDialog;
import androidx.core.util.Consumer;
import app.organicmaps.R;
import app.organicmaps.sdk.downloader.CountryItem;
import app.organicmaps.sdk.downloader.ExpandRetryConfirmationListener;
import app.organicmaps.sdk.downloader.MapManager;
import app.organicmaps.sdk.util.ConnectionState;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import java.lang.ref.WeakReference;
public class MapManagerHelper
{
private static WeakReference<AlertDialog> sCurrentErrorDialog;
@StringRes
public static int getErrorCodeStrRes(final int errorCode)
{
return switch (errorCode)
{
case CountryItem.ERROR_NO_INTERNET -> R.string.common_check_internet_connection_dialog;
case CountryItem.ERROR_OOM -> R.string.downloader_no_space_title;
default -> throw new IllegalArgumentException("Given error can not be displayed: " + errorCode);
};
}
public static void showError(final Activity activity, final MapManager.StorageCallbackData errorData,
@Nullable final Consumer<Boolean> dialogClickListener)
{
if (!MapManager.nativeIsAutoretryFailed())
return;
showErrorDialog(activity, errorData, dialogClickListener);
}
public static void showErrorDialog(final Activity activity, final MapManager.StorageCallbackData errorData,
@Nullable final Consumer<Boolean> dialogClickListener)
{
if (sCurrentErrorDialog != null)
{
AlertDialog dlg = sCurrentErrorDialog.get();
if (dlg != null && dlg.isShowing())
return;
}
final AlertDialog dlg = new MaterialAlertDialogBuilder(activity, R.style.MwmTheme_AlertDialog)
.setTitle(R.string.country_status_download_failed)
.setMessage(getErrorCodeStrRes(errorData.errorCode))
.setNegativeButton(R.string.cancel,
(dialog, which) -> {
sCurrentErrorDialog = null;
if (dialogClickListener != null)
dialogClickListener.accept(false);
})
.setPositiveButton(R.string.downloader_retry,
(dialog, which) -> {
ExpandRetryConfirmationListener listener =
new ExpandRetryConfirmationListener(dialogClickListener);
warn3gAndRetry(activity, errorData.countryId, listener);
})
.create();
dlg.setCanceledOnTouchOutside(false);
dlg.show();
sCurrentErrorDialog = new WeakReference<>(dlg);
}
private static void notifyNoSpaceInternal(Activity activity)
{
new MaterialAlertDialogBuilder(activity, R.style.MwmTheme_AlertDialog)
.setTitle(R.string.downloader_no_space_title)
.setMessage(R.string.downloader_no_space_message)
.setPositiveButton(android.R.string.ok, null)
.show();
}
/**
* @return true if there is no space to update the given {@code root}, so the alert dialog will be shown.
*/
private static boolean notifyNoSpaceToUpdate(Activity activity, String root)
{
if (MapManager.nativeHasSpaceToUpdate(root))
return false;
notifyNoSpaceInternal(activity);
return true;
}
/**
* @return true if there is no space to download the given {@code root}, so the alert dialog will be shown.
*/
private static boolean notifyNoSpace(Activity activity, String root)
{
if (MapManager.nativeHasSpaceToDownloadCountry(root))
return false;
notifyNoSpaceInternal(activity);
return true;
}
/**
* @return true if there is no space to download {@code size} bytes, so the alert dialog will be shown.
*/
private static boolean notifyNoSpace(Activity activity, long size)
{
if (MapManager.nativeHasSpaceToDownloadAmount(size))
return false;
notifyNoSpaceInternal(activity);
return true;
}
private static boolean warnOn3gInternal(Activity activity, @NonNull final Runnable onAcceptListener)
{
if (MapManager.nativeIsDownloadOn3gEnabled() || !ConnectionState.INSTANCE.isMobileConnected())
{
onAcceptListener.run();
return false;
}
new MaterialAlertDialogBuilder(activity, R.style.MwmTheme_AlertDialog)
.setTitle(R.string.download_over_mobile_header)
.setMessage(R.string.download_over_mobile_message)
.setNegativeButton(R.string.cancel, null)
.setPositiveButton(R.string.ok,
(dlg, which) -> {
MapManager.nativeEnableDownloadOn3g();
onAcceptListener.run();
})
.show();
return true;
}
public static boolean warnOn3gUpdate(Activity activity, @Nullable String countryId,
@NonNull final Runnable onAcceptListener)
{
// noinspection SimplifiableIfStatement
if (TextUtils.isEmpty(countryId) || !notifyNoSpaceToUpdate(activity, countryId))
return warnOn3gInternal(activity, onAcceptListener);
return true;
}
public static boolean warnOn3g(Activity activity, @Nullable String countryId,
@NonNull final Runnable onAcceptListener)
{
// noinspection SimplifiableIfStatement
if (TextUtils.isEmpty(countryId) || !notifyNoSpace(activity, countryId))
return warnOn3gInternal(activity, onAcceptListener);
return true;
}
public static boolean warnOn3g(Activity activity, long size, @NonNull Runnable onAcceptListener)
{
return !notifyNoSpace(activity, size) && warnOn3gInternal(activity, onAcceptListener);
}
public static boolean warn3gAndDownload(Activity activity, final String countryId,
@Nullable final Runnable onAcceptListener)
{
return warnOn3g(activity, countryId, () -> {
if (onAcceptListener != null)
onAcceptListener.run();
startDownload(countryId);
});
}
public static boolean warn3gAndRetry(Activity activity, final String countryId,
@Nullable final Runnable onAcceptListener)
{
return warnOn3g(activity, countryId, () -> {
if (onAcceptListener != null)
onAcceptListener.run();
retryDownload(countryId);
});
}
/**
* Enqueues failed items under given {@code root} node in downloader.
*/
public static void retryDownload(@NonNull String countryId)
{
DownloaderService.startForegroundService();
MapManager.retryDownload(countryId);
}
/**
* Enqueues given {@code root} node with its children in downloader.
*/
public static void startUpdate(@NonNull String root)
{
DownloaderService.startForegroundService();
MapManager.startUpdate(root);
}
/**
* Enqueues the given list of nodes and its children in downloader.
*/
public static void startDownload(String... countries)
{
DownloaderService.startForegroundService();
for (var countryId : countries)
{
MapManager.startDownload(countryId);
}
}
/**
* Enqueues given {@code root} node and its children in downloader.
*/
public static void startDownload(@NonNull String countryId)
{
DownloaderService.startForegroundService();
MapManager.startDownload(countryId);
}
}

View File

@@ -9,13 +9,13 @@ import androidx.core.view.ViewCompat;
import app.organicmaps.MwmActivity; import app.organicmaps.MwmActivity;
import app.organicmaps.MwmApplication; import app.organicmaps.MwmApplication;
import app.organicmaps.R; import app.organicmaps.R;
import app.organicmaps.routing.RoutingController;
import app.organicmaps.sdk.downloader.CountryItem; import app.organicmaps.sdk.downloader.CountryItem;
import app.organicmaps.sdk.downloader.MapManager; import app.organicmaps.sdk.downloader.MapManager;
import app.organicmaps.sdk.routing.RoutingController;
import app.organicmaps.sdk.util.Config; import app.organicmaps.sdk.util.Config;
import app.organicmaps.sdk.util.ConnectionState; import app.organicmaps.sdk.util.ConnectionState;
import app.organicmaps.sdk.util.StringUtils; import app.organicmaps.sdk.util.StringUtils;
import app.organicmaps.util.UiUtils; import app.organicmaps.sdk.util.UiUtils;
import app.organicmaps.util.WindowInsetUtils.PaddingInsetsListener; import app.organicmaps.util.WindowInsetUtils.PaddingInsetsListener;
import app.organicmaps.widget.WheelProgressView; import app.organicmaps.widget.WheelProgressView;
import com.google.android.material.button.MaterialButton; import com.google.android.material.button.MaterialButton;
@@ -52,7 +52,7 @@ public class OnmapDownloader implements MwmActivity.LeftAnimationTrackListener
continue; continue;
if (item.newStatus == CountryItem.STATUS_FAILED) if (item.newStatus == CountryItem.STATUS_FAILED)
MapManagerHelper.showError(mActivity, item, null); MapManager.showError(mActivity, item, null);
if (mCurrentCountry.id.equals(item.countryId)) if (mCurrentCountry.id.equals(item.countryId))
{ {
@@ -163,7 +163,7 @@ public class OnmapDownloader implements MwmActivity.LeftAnimationTrackListener
if (TextUtils.equals(mCurrentCountry.id, country) if (TextUtils.equals(mCurrentCountry.id, country)
&& MapManager.nativeHasSpaceToDownloadCountry(country)) && MapManager.nativeHasSpaceToDownloadCountry(country))
{ {
MapManagerHelper.startDownload(mCurrentCountry.id); MapManager.startDownload(mCurrentCountry.id);
} }
} }
} }
@@ -199,18 +199,18 @@ public class OnmapDownloader implements MwmActivity.LeftAnimationTrackListener
setAutodownloadLocked(true); setAutodownloadLocked(true);
}); });
mButton.setOnClickListener( mButton.setOnClickListener(
v -> MapManagerHelper.warnOn3g(mActivity, mCurrentCountry == null ? null : mCurrentCountry.id, () -> { v -> MapManager.warnOn3g(mActivity, mCurrentCountry == null ? null : mCurrentCountry.id, () -> {
if (mCurrentCountry == null) if (mCurrentCountry == null)
return; return;
boolean retry = (mCurrentCountry.status == CountryItem.STATUS_FAILED); boolean retry = (mCurrentCountry.status == CountryItem.STATUS_FAILED);
if (retry) if (retry)
{ {
MapManagerHelper.retryDownload(mCurrentCountry.id); MapManager.retryDownload(mCurrentCountry.id);
} }
else else
{ {
MapManagerHelper.startDownload(mCurrentCountry.id); MapManager.startDownload(mCurrentCountry.id);
mActivity.requestPostNotificationsPermission(); mActivity.requestPostNotificationsPermission();
} }
})); }));

View File

@@ -14,9 +14,9 @@ import app.organicmaps.R;
import app.organicmaps.base.BaseMwmFragment; import app.organicmaps.base.BaseMwmFragment;
import app.organicmaps.sdk.editor.OpeningHours; import app.organicmaps.sdk.editor.OpeningHours;
import app.organicmaps.sdk.util.Constants; import app.organicmaps.sdk.util.Constants;
import app.organicmaps.sdk.util.UiUtils;
import app.organicmaps.util.Graphics; import app.organicmaps.util.Graphics;
import app.organicmaps.util.InputUtils; import app.organicmaps.util.InputUtils;
import app.organicmaps.util.UiUtils;
import com.google.android.material.imageview.ShapeableImageView; import com.google.android.material.imageview.ShapeableImageView;
import com.google.android.material.textfield.TextInputEditText; import com.google.android.material.textfield.TextInputEditText;
import com.google.android.material.textview.MaterialTextView; import com.google.android.material.textview.MaterialTextView;
@@ -87,8 +87,8 @@ public class AdvancedTimetableFragment extends BaseMwmFragment implements View.O
private void setExampleDrawables(@DrawableRes int left, @DrawableRes int right) private void setExampleDrawables(@DrawableRes int left, @DrawableRes int right)
{ {
mExamplesTitle.setCompoundDrawablesRelativeWithIntrinsicBounds( mExamplesTitle.setCompoundDrawablesRelativeWithIntrinsicBounds(
Graphics.tint(requireActivity(), left, com.google.android.material.R.attr.colorSecondary), null, Graphics.tint(requireActivity(), left, androidx.appcompat.R.attr.colorAccent), null,
Graphics.tint(requireActivity(), right, com.google.android.material.R.attr.colorSecondary), null); Graphics.tint(requireActivity(), right, androidx.appcompat.R.attr.colorAccent), null);
} }
@Override @Override

View File

@@ -20,19 +20,19 @@ import androidx.recyclerview.widget.RecyclerView;
import app.organicmaps.R; import app.organicmaps.R;
import app.organicmaps.base.BaseMwmFragment; import app.organicmaps.base.BaseMwmFragment;
import app.organicmaps.dialog.EditTextDialogFragment; import app.organicmaps.dialog.EditTextDialogFragment;
import app.organicmaps.editor.data.TimeFormatUtils;
import app.organicmaps.sdk.Framework; import app.organicmaps.sdk.Framework;
import app.organicmaps.sdk.bookmarks.data.Metadata; import app.organicmaps.sdk.bookmarks.data.Metadata;
import app.organicmaps.sdk.editor.Editor; import app.organicmaps.sdk.editor.Editor;
import app.organicmaps.sdk.editor.OpeningHours; import app.organicmaps.sdk.editor.OpeningHours;
import app.organicmaps.sdk.editor.data.LocalizedName; import app.organicmaps.sdk.editor.data.LocalizedName;
import app.organicmaps.sdk.editor.data.LocalizedStreet; import app.organicmaps.sdk.editor.data.LocalizedStreet;
import app.organicmaps.editor.data.TimeFormatUtils;
import app.organicmaps.sdk.editor.data.Timetable; import app.organicmaps.sdk.editor.data.Timetable;
import app.organicmaps.sdk.util.StringUtils; import app.organicmaps.sdk.util.StringUtils;
import app.organicmaps.sdk.util.UiUtils;
import app.organicmaps.sdk.util.Utils; import app.organicmaps.sdk.util.Utils;
import app.organicmaps.util.Graphics; import app.organicmaps.util.Graphics;
import app.organicmaps.util.InputUtils; import app.organicmaps.util.InputUtils;
import app.organicmaps.util.UiUtils;
import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.imageview.ShapeableImageView; import com.google.android.material.imageview.ShapeableImageView;
import com.google.android.material.textfield.TextInputEditText; import com.google.android.material.textfield.TextInputEditText;
@@ -236,11 +236,6 @@ public class EditorFragment extends BaseMwmFragment implements View.OnClickListe
return true; return true;
} }
boolean saveEdits()
{
return setEdits() && beforeSavingValidation();
}
@NonNull @NonNull
protected String getDescription() protected String getDescription()
{ {
@@ -287,19 +282,6 @@ public class EditorFragment extends BaseMwmFragment implements View.OnClickListe
return validateNames(); return validateNames();
} }
private boolean beforeSavingValidation()
{
// Validation to make sure address features have a house number
if (!Editor.nativeCheckHouseNumberWhenIsAddress())
{
mHouseNumber.requestFocus();
UiUtils.setInputError(mInputHouseNumber, R.string.error_enter_correct_house_number);
InputUtils.showKeyboard(mHouseNumber);
return false;
}
return true;
}
private boolean validateNames() private boolean validateNames()
{ {
for (int pos = 0; pos < mNamesAdapter.getItemCount(); pos++) for (int pos = 0; pos < mNamesAdapter.getItemCount(); pos++)
@@ -641,11 +623,11 @@ public class EditorFragment extends BaseMwmFragment implements View.OnClickListe
switch (Editor.nativeGetMapObjectStatus()) switch (Editor.nativeGetMapObjectStatus())
{ {
case Editor.CREATED -> mReset.setText(R.string.editor_remove_place_button); case Editor.CREATED -> mReset.setText(R.string.editor_remove_place_button);
case Editor.MODIFIED -> mReset.setText(R.string.editor_reset_edits_button); case Editor.MODIFIED -> mReset.setText(R.string.editor_reset_edits_button);
case Editor.UNTOUCHED -> mReset.setText(R.string.editor_place_doesnt_exist); case Editor.UNTOUCHED -> mReset.setText(R.string.editor_place_doesnt_exist);
case Editor.DELETED -> throw new IllegalStateException("Can't delete already deleted feature."); case Editor.DELETED -> throw new IllegalStateException("Can't delete already deleted feature.");
case Editor.OBSOLETE -> throw new IllegalStateException("Obsolete objects cannot be reverted."); case Editor.OBSOLETE -> throw new IllegalStateException("Obsolete objects cannot be reverted.");
} }
} }
@@ -659,11 +641,11 @@ public class EditorFragment extends BaseMwmFragment implements View.OnClickListe
switch (Editor.nativeGetMapObjectStatus()) switch (Editor.nativeGetMapObjectStatus())
{ {
case Editor.CREATED -> rollback(Editor.CREATED); case Editor.CREATED -> rollback(Editor.CREATED);
case Editor.MODIFIED -> rollback(Editor.MODIFIED); case Editor.MODIFIED -> rollback(Editor.MODIFIED);
case Editor.UNTOUCHED -> placeDoesntExist(); case Editor.UNTOUCHED -> placeDoesntExist();
case Editor.DELETED -> throw new IllegalStateException("Can't delete already deleted feature."); case Editor.DELETED -> throw new IllegalStateException("Can't delete already deleted feature.");
case Editor.OBSOLETE -> throw new IllegalStateException("Obsolete objects cannot be reverted."); case Editor.OBSOLETE -> throw new IllegalStateException("Obsolete objects cannot be reverted.");
} }
} }

View File

@@ -1,5 +1,6 @@
package app.organicmaps.editor; package app.organicmaps.editor;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.text.TextUtils; import android.text.TextUtils;
@@ -24,7 +25,7 @@ import app.organicmaps.sdk.editor.data.Language;
import app.organicmaps.sdk.editor.data.LocalizedName; import app.organicmaps.sdk.editor.data.LocalizedName;
import app.organicmaps.sdk.editor.data.LocalizedStreet; import app.organicmaps.sdk.editor.data.LocalizedStreet;
import app.organicmaps.sdk.editor.data.NamesDataSource; import app.organicmaps.sdk.editor.data.NamesDataSource;
import app.organicmaps.util.UiUtils; import app.organicmaps.sdk.util.UiUtils;
import app.organicmaps.util.Utils; import app.organicmaps.util.Utils;
import app.organicmaps.util.WindowInsetUtils.PaddingInsetsListener; import app.organicmaps.util.WindowInsetUtils.PaddingInsetsListener;
import app.organicmaps.widget.SearchToolbarController; import app.organicmaps.widget.SearchToolbarController;
@@ -172,8 +173,8 @@ public class EditorHostFragment
{ {
switch (mMode) switch (mMode)
{ {
case OPENING_HOURS, STREET, CUISINE, LANGUAGE, PHONE, SELF_SERVICE -> editMapObject(); case OPENING_HOURS, STREET, CUISINE, LANGUAGE, PHONE, SELF_SERVICE -> editMapObject();
default -> Utils.navigateToParent(requireActivity()); default -> Utils.navigateToParent(requireActivity());
} }
return true; return true;
} }
@@ -275,11 +276,6 @@ public class EditorHostFragment
return ((EditorFragment) getChildFragmentManager().findFragmentByTag(EditorFragment.class.getName())).setEdits(); return ((EditorFragment) getChildFragmentManager().findFragmentByTag(EditorFragment.class.getName())).setEdits();
} }
private boolean saveEdits()
{
return ((EditorFragment) getChildFragmentManager().findFragmentByTag(EditorFragment.class.getName())).saveEdits();
}
@Override @Override
public void onClick(View v) public void onClick(View v)
{ {
@@ -287,57 +283,57 @@ public class EditorHostFragment
{ {
switch (mMode) switch (mMode)
{ {
case OPENING_HOURS -> case OPENING_HOURS ->
{
final String timetables = ((TimetableContainerFragment) getChildFragmentManager().findFragmentByTag(
TimetableContainerFragment.class.getName()))
.getTimetable();
Editor.nativeSetOpeningHours(timetables);
editMapObject();
}
case STREET ->
setStreet(
((StreetFragment) getChildFragmentManager().findFragmentByTag(StreetFragment.class.getName())).getStreet());
case CUISINE ->
{
String[] cuisines =
((CuisineFragment) getChildFragmentManager().findFragmentByTag(CuisineFragment.class.getName()))
.getCuisines();
Editor.nativeSetSelectedCuisines(cuisines);
editMapObject();
}
case SELF_SERVICE ->
setSelection(
Metadata.MetadataType.FMD_SELF_SERVICE,
((SelfServiceFragment) getChildFragmentManager().findFragmentByTag(SelfServiceFragment.class.getName()))
.getSelection());
case LANGUAGE -> editMapObject();
case MAP_OBJECT ->
{
if (!saveEdits())
return;
// Save object edits
if (!MwmApplication.prefs(requireContext()).contains(NOOB_ALERT_SHOWN))
{ {
showNoobDialog(); final String timetables = ((TimetableContainerFragment) getChildFragmentManager().findFragmentByTag(
} TimetableContainerFragment.class.getName()))
else .getTimetable();
{ Editor.nativeSetOpeningHours(timetables);
saveNote();
saveMapObjectEdits();
}
}
case PHONE ->
{
final String phone =
((PhoneFragment) getChildFragmentManager().findFragmentByTag(PhoneFragment.class.getName())).getPhone();
if (Editor.nativeIsPhoneValid(phone))
{
Editor.nativeSetPhone(phone);
editMapObject(); editMapObject();
} }
} case STREET ->
setStreet(((StreetFragment) getChildFragmentManager().findFragmentByTag(StreetFragment.class.getName()))
.getStreet());
case CUISINE ->
{
String[] cuisines =
((CuisineFragment) getChildFragmentManager().findFragmentByTag(CuisineFragment.class.getName()))
.getCuisines();
Editor.nativeSetSelectedCuisines(cuisines);
editMapObject();
}
case SELF_SERVICE ->
setSelection(
Metadata.MetadataType.FMD_SELF_SERVICE,
((SelfServiceFragment) getChildFragmentManager().findFragmentByTag(SelfServiceFragment.class.getName()))
.getSelection());
case LANGUAGE -> editMapObject();
case MAP_OBJECT ->
{
if (!setEdits())
return;
// Save object edits
if (!MwmApplication.prefs(requireContext()).contains(NOOB_ALERT_SHOWN))
{
showNoobDialog();
}
else
{
saveNote();
saveMapObjectEdits();
}
}
case PHONE ->
{
final String phone =
((PhoneFragment) getChildFragmentManager().findFragmentByTag(PhoneFragment.class.getName())).getPhone();
if (Editor.nativeIsPhoneValid(phone))
{
Editor.nativeSetPhone(phone);
editMapObject();
}
}
} }
} }
} }

View File

@@ -9,9 +9,7 @@ import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import app.organicmaps.R; import app.organicmaps.R;
import app.organicmaps.sdk.editor.data.FeatureCategory; import app.organicmaps.sdk.editor.data.FeatureCategory;
import app.organicmaps.sdk.util.StringUtils; import app.organicmaps.sdk.util.UiUtils;
import app.organicmaps.util.UiUtils;
import com.google.android.material.textfield.TextInputEditText;
import com.google.android.material.textview.MaterialTextView; import com.google.android.material.textview.MaterialTextView;
public class FeatureCategoryAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> public class FeatureCategoryAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
@@ -23,12 +21,6 @@ public class FeatureCategoryAdapter extends RecyclerView.Adapter<RecyclerView.Vi
private final FeatureCategoryFragment mFragment; private final FeatureCategoryFragment mFragment;
private final FeatureCategory mSelectedCategory; private final FeatureCategory mSelectedCategory;
public interface FooterListener
{
void onNoteTextChanged(String newText);
void onSendNoteClicked();
}
public FeatureCategoryAdapter(@NonNull FeatureCategoryFragment host, @NonNull FeatureCategory[] categories, public FeatureCategoryAdapter(@NonNull FeatureCategoryFragment host, @NonNull FeatureCategory[] categories,
@Nullable FeatureCategory category) @Nullable FeatureCategory category)
{ {
@@ -55,23 +47,19 @@ public class FeatureCategoryAdapter extends RecyclerView.Adapter<RecyclerView.Vi
@Override @Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
{ {
final LayoutInflater inflater = LayoutInflater.from(parent.getContext());
switch (viewType) switch (viewType)
{ {
case TYPE_CATEGORY -> case TYPE_CATEGORY ->
{ {
return new FeatureViewHolder(inflater.inflate(R.layout.item_feature_category, parent, false)); return new FeatureViewHolder(
} LayoutInflater.from(parent.getContext()).inflate(R.layout.item_feature_category, parent, false));
case TYPE_FOOTER -> }
{ case TYPE_FOOTER ->
return new FooterViewHolder(inflater.inflate(R.layout.item_feature_category_footer, parent, false), {
(FooterListener) mFragment); return new FooterViewHolder(
} LayoutInflater.from(parent.getContext()).inflate(R.layout.item_feature_category_footer, parent, false));
default -> }
{ default -> throw new IllegalArgumentException("Unsupported");
throw new IllegalArgumentException("Unsupported viewType: " + viewType);
}
} }
} }
@@ -82,10 +70,6 @@ public class FeatureCategoryAdapter extends RecyclerView.Adapter<RecyclerView.Vi
{ {
((FeatureViewHolder) holder).bind(position); ((FeatureViewHolder) holder).bind(position);
} }
else if (holder instanceof FooterViewHolder)
{
((FooterViewHolder) holder).bind(mFragment.getPendingNoteText());
}
} }
@Override @Override
@@ -121,36 +105,11 @@ public class FeatureCategoryAdapter extends RecyclerView.Adapter<RecyclerView.Vi
protected static class FooterViewHolder extends RecyclerView.ViewHolder protected static class FooterViewHolder extends RecyclerView.ViewHolder
{ {
private final TextInputEditText mNoteEditText; FooterViewHolder(@NonNull View itemView)
private final View mSendNoteButton;
FooterViewHolder(@NonNull View itemView, @NonNull FooterListener listener)
{ {
super(itemView); super(itemView);
MaterialTextView categoryUnsuitableText = itemView.findViewById(R.id.editor_category_unsuitable_text); MaterialTextView categoryUnsuitableText = itemView.findViewById(R.id.editor_category_unsuitable_text);
categoryUnsuitableText.setMovementMethod(LinkMovementMethod.getInstance()); categoryUnsuitableText.setMovementMethod(LinkMovementMethod.getInstance());
mNoteEditText = itemView.findViewById(R.id.note_edit_text);
mSendNoteButton = itemView.findViewById(R.id.send_note_button);
mSendNoteButton.setOnClickListener(v -> listener.onSendNoteClicked());
mNoteEditText.addTextChangedListener(new StringUtils.SimpleTextWatcher() {
@Override
public void onTextChanged(CharSequence s, int start, int before, int count)
{
final String str = s.toString();
listener.onNoteTextChanged(str);
mSendNoteButton.setEnabled(!str.trim().isEmpty());
}
});
}
public void bind(String pendingNoteText)
{
if (!mNoteEditText.getText().toString().equals(pendingNoteText))
{
mNoteEditText.setText(pendingNoteText);
if (pendingNoteText != null)
mNoteEditText.setSelection(pendingNoteText.length());
}
mSendNoteButton.setEnabled(pendingNoteText != null && !pendingNoteText.trim().isEmpty());
} }
} }

View File

@@ -2,39 +2,28 @@ package app.organicmaps.editor;
import static app.organicmaps.sdk.util.Utils.getLocalizedFeatureType; import static app.organicmaps.sdk.util.Utils.getLocalizedFeatureType;
import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.text.TextUtils;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.Toast;
import androidx.annotation.CallSuper; import androidx.annotation.CallSuper;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import app.organicmaps.MwmApplication;
import app.organicmaps.R; import app.organicmaps.R;
import app.organicmaps.base.BaseMwmRecyclerFragment; import app.organicmaps.base.BaseMwmRecyclerFragment;
import app.organicmaps.dialog.EditTextDialogFragment;
import app.organicmaps.sdk.Framework;
import app.organicmaps.sdk.editor.Editor; import app.organicmaps.sdk.editor.Editor;
import app.organicmaps.sdk.editor.OsmOAuth;
import app.organicmaps.sdk.editor.data.FeatureCategory; import app.organicmaps.sdk.editor.data.FeatureCategory;
import app.organicmaps.sdk.util.Language; import app.organicmaps.sdk.util.Language;
import app.organicmaps.util.Utils; import app.organicmaps.util.Utils;
import app.organicmaps.widget.SearchToolbarController; import app.organicmaps.widget.SearchToolbarController;
import app.organicmaps.widget.ToolbarController; import app.organicmaps.widget.ToolbarController;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import java.util.Arrays; import java.util.Arrays;
import java.util.Comparator; import java.util.Comparator;
public class FeatureCategoryFragment public class FeatureCategoryFragment extends BaseMwmRecyclerFragment<FeatureCategoryAdapter>
extends BaseMwmRecyclerFragment<FeatureCategoryAdapter> implements FeatureCategoryAdapter.FooterListener
{ {
private FeatureCategory mSelectedCategory; private FeatureCategory mSelectedCategory;
protected ToolbarController mToolbarController; protected ToolbarController mToolbarController;
private static final String NOTE_CONFIRMATION_SHOWN = "NoteConfirmationAlertWasShown";
private static String mPendingNoteText = "";
public interface FeatureCategoryListener public interface FeatureCategoryListener
{ {
@@ -115,71 +104,4 @@ public class FeatureCategoryFragment
else if (getParentFragment() instanceof FeatureCategoryListener) else if (getParentFragment() instanceof FeatureCategoryListener)
((FeatureCategoryListener) getParentFragment()).onFeatureCategorySelected(category); ((FeatureCategoryListener) getParentFragment()).onFeatureCategorySelected(category);
} }
public String getPendingNoteText()
{
return mPendingNoteText;
}
@Override
public void onNoteTextChanged(String newText)
{
mPendingNoteText = newText;
}
@Override
public void onSendNoteClicked()
{
final double[] center = Framework.nativeGetScreenRectCenter();
final double lat = center[0];
final double lon = center[1];
if (!MwmApplication.prefs(requireContext().getApplicationContext()).contains(NOTE_CONFIRMATION_SHOWN))
{
showNoteConfirmationDialog(lat, lon, mPendingNoteText);
}
else
{
Editor.nativeCreateStandaloneNote(lat, lon, mPendingNoteText);
mPendingNoteText = "";
Toast.makeText(requireContext(), R.string.osm_note_toast, Toast.LENGTH_SHORT).show();
if (!OsmOAuth.isAuthorized())
{
final Intent intent = new Intent(requireActivity(), OsmLoginActivity.class);
startActivity(intent);
}
requireActivity().finish();
}
}
// Duplicate of showNoobDialog()
private void showNoteConfirmationDialog(double lat, double lon, String noteText)
{
new MaterialAlertDialogBuilder(requireActivity(), R.style.MwmTheme_AlertDialog)
.setTitle(R.string.editor_share_to_all_dialog_title)
.setMessage(getString(R.string.editor_share_to_all_dialog_message_1) + " "
+ getString(R.string.editor_share_to_all_dialog_message_2))
.setPositiveButton(android.R.string.ok,
(dlg, which) -> {
MwmApplication.prefs(requireContext().getApplicationContext())
.edit()
.putBoolean(NOTE_CONFIRMATION_SHOWN, true)
.apply();
Editor.nativeCreateStandaloneNote(lat, lon, noteText);
mPendingNoteText = "";
Toast.makeText(requireContext(), R.string.osm_note_toast, Toast.LENGTH_SHORT).show();
if (!OsmOAuth.isAuthorized())
{
final Intent intent = new Intent(requireActivity(), OsmLoginActivity.class);
startActivity(intent);
}
requireActivity().finish();
})
.setNegativeButton(R.string.cancel, null)
.show();
}
} }

View File

@@ -79,8 +79,8 @@ public class HoursMinutesPickerFragment extends BaseMwmDialogFragment
mTabs.getTabAt(mSelectedTab).select(); mTabs.getTabAt(mSelectedTab).select();
@StyleRes @StyleRes
final int theme = ThemeUtils.isNightTheme() ? R.style.MwmMain_DialogFragment_TimePicker_Night final int theme = ThemeUtils.isNightTheme(requireContext()) ? R.style.MwmMain_DialogFragment_TimePicker_Night
: R.style.MwmMain_DialogFragment_TimePicker; : R.style.MwmMain_DialogFragment_TimePicker;
final AlertDialog dialog = new MaterialAlertDialogBuilder(requireActivity(), theme) final AlertDialog dialog = new MaterialAlertDialogBuilder(requireActivity(), theme)
.setView(root) .setView(root)
.setNegativeButton(R.string.cancel, null) .setNegativeButton(R.string.cancel, null)
@@ -141,15 +141,15 @@ public class HoursMinutesPickerFragment extends BaseMwmDialogFragment
MaterialTextView tabView = (MaterialTextView) inflater.inflate(R.layout.tab_timepicker, mTabs, false); MaterialTextView tabView = (MaterialTextView) inflater.inflate(R.layout.tab_timepicker, mTabs, false);
tabView.setText(getResources().getString(R.string.editor_time_from)); tabView.setText(getResources().getString(R.string.editor_time_from));
final ColorStateList textColor = AppCompatResources.getColorStateList( final ColorStateList textColor = AppCompatResources.getColorStateList(
requireContext(), requireContext(), ThemeUtils.isNightTheme(requireContext()) ? R.color.accent_color_selector_night
ThemeUtils.isNightTheme() ? R.color.accent_color_selector_night : R.color.accent_color_selector); : R.color.accent_color_selector);
tabView.setTextColor(textColor); tabView.setTextColor(textColor);
mTabs.addTab(mTabs.newTab().setCustomView(tabView), true); mTabs.addTab(mTabs.newTab().setCustomView(tabView), true);
tabView = (MaterialTextView) inflater.inflate(R.layout.tab_timepicker, mTabs, false); tabView = (MaterialTextView) inflater.inflate(R.layout.tab_timepicker, mTabs, false);
tabView.setText(getResources().getString(R.string.editor_time_to)); tabView.setText(getResources().getString(R.string.editor_time_to));
tabView.setTextColor(textColor); tabView.setTextColor(textColor);
mTabs.addTab(mTabs.newTab().setCustomView(tabView), true); mTabs.addTab(mTabs.newTab().setCustomView(tabView), true);
mTabs.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() { mTabs.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override @Override
public void onTabSelected(TabLayout.Tab tab) public void onTabSelected(TabLayout.Tab tab)
{ {

View File

@@ -10,7 +10,7 @@ import app.organicmaps.sdk.editor.Editor;
import app.organicmaps.sdk.editor.data.Language; import app.organicmaps.sdk.editor.data.Language;
import app.organicmaps.sdk.editor.data.LocalizedName; import app.organicmaps.sdk.editor.data.LocalizedName;
import app.organicmaps.sdk.util.StringUtils; import app.organicmaps.sdk.util.StringUtils;
import app.organicmaps.util.UiUtils; import app.organicmaps.sdk.util.UiUtils;
import app.organicmaps.util.Utils; import app.organicmaps.util.Utils;
import com.google.android.material.textfield.TextInputEditText; import com.google.android.material.textfield.TextInputEditText;
import com.google.android.material.textfield.TextInputLayout; import com.google.android.material.textfield.TextInputLayout;

View File

@@ -1,4 +1,4 @@
package app.organicmaps.sdk.editor; package app.organicmaps.editor;
import androidx.annotation.Keep; import androidx.annotation.Keep;

View File

@@ -15,11 +15,10 @@ import app.organicmaps.R;
import app.organicmaps.base.BaseMwmToolbarFragment; import app.organicmaps.base.BaseMwmToolbarFragment;
import app.organicmaps.sdk.editor.OsmOAuth; import app.organicmaps.sdk.editor.OsmOAuth;
import app.organicmaps.sdk.util.Constants; import app.organicmaps.sdk.util.Constants;
import app.organicmaps.sdk.util.DateUtils; import app.organicmaps.sdk.util.UiUtils;
import app.organicmaps.sdk.util.concurrency.ThreadPool; import app.organicmaps.sdk.util.concurrency.ThreadPool;
import app.organicmaps.sdk.util.concurrency.UiThread; import app.organicmaps.sdk.util.concurrency.UiThread;
import app.organicmaps.util.InputUtils; import app.organicmaps.util.InputUtils;
import app.organicmaps.util.UiUtils;
import app.organicmaps.util.Utils; import app.organicmaps.util.Utils;
import app.organicmaps.util.WindowInsetUtils.ScrollableContentInsetsListener; import app.organicmaps.util.WindowInsetUtils.ScrollableContentInsetsListener;
import com.google.android.material.button.MaterialButton; import com.google.android.material.button.MaterialButton;

View File

@@ -9,7 +9,7 @@ import androidx.recyclerview.widget.RecyclerView;
import app.organicmaps.R; import app.organicmaps.R;
import app.organicmaps.sdk.editor.Editor; import app.organicmaps.sdk.editor.Editor;
import app.organicmaps.sdk.util.StringUtils; import app.organicmaps.sdk.util.StringUtils;
import app.organicmaps.util.UiUtils; import app.organicmaps.sdk.util.UiUtils;
import com.google.android.material.imageview.ShapeableImageView; import com.google.android.material.imageview.ShapeableImageView;
import com.google.android.material.textfield.TextInputEditText; import com.google.android.material.textfield.TextInputEditText;
import com.google.android.material.textfield.TextInputLayout; import com.google.android.material.textfield.TextInputLayout;

View File

@@ -11,17 +11,14 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.core.view.ViewCompat; import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat; import androidx.core.view.WindowInsetsCompat;
import androidx.fragment.app.FragmentManager;
import app.organicmaps.R; import app.organicmaps.R;
import app.organicmaps.base.BaseMwmToolbarFragment; import app.organicmaps.base.BaseMwmToolbarFragment;
import app.organicmaps.sdk.editor.OsmOAuth; import app.organicmaps.sdk.editor.OsmOAuth;
import app.organicmaps.sdk.util.NetworkPolicy; import app.organicmaps.sdk.util.UiUtils;
import app.organicmaps.sdk.util.concurrency.ThreadPool; import app.organicmaps.sdk.util.concurrency.ThreadPool;
import app.organicmaps.sdk.util.concurrency.UiThread; import app.organicmaps.sdk.util.concurrency.UiThread;
import app.organicmaps.util.UiUtils;
import app.organicmaps.util.Utils; import app.organicmaps.util.Utils;
import app.organicmaps.util.WindowInsetUtils; import app.organicmaps.util.WindowInsetUtils;
import app.organicmaps.widget.StackedButtonDialogFragment;
import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.imageview.ShapeableImageView; import com.google.android.material.imageview.ShapeableImageView;
import com.google.android.material.textview.MaterialTextView; import com.google.android.material.textview.MaterialTextView;
@@ -29,24 +26,6 @@ import java.text.NumberFormat;
public class ProfileFragment extends BaseMwmToolbarFragment public class ProfileFragment extends BaseMwmToolbarFragment
{ {
@NonNull
private static final NetworkPolicy.DialogPresenter mDialogPresenter = new NetworkPolicy.DialogPresenter() {
@Override
public void showDialogIfNeeded(@NonNull FragmentManager fragmentManager,
@NonNull NetworkPolicy.NetworkPolicyListener listener, @NonNull NetworkPolicy policy,
boolean isToday)
{
StackedButtonDialogFragment.showDialogIfNeeded(fragmentManager, listener, policy, isToday);
}
@Override
public void showDialog(@NonNull FragmentManager fragmentManager,
@NonNull NetworkPolicy.NetworkPolicyListener listener)
{
StackedButtonDialogFragment.showDialog(fragmentManager, listener);
}
};
private View mUserInfoBlock; private View mUserInfoBlock;
private MaterialTextView mEditsSent; private MaterialTextView mEditsSent;
private MaterialTextView mProfileName; private MaterialTextView mProfileName;
@@ -105,7 +84,7 @@ public class ProfileFragment extends BaseMwmToolbarFragment
UiUtils.show(mProfileInfoLoading); UiUtils.show(mProfileInfoLoading);
UiUtils.hide(mUserInfoBlock); UiUtils.hide(mUserInfoBlock);
} }
final int profileEditCount = OsmOAuth.getOsmChangesetsCount(mDialogPresenter, getParentFragmentManager()); final int profileEditCount = OsmOAuth.getOsmChangesetsCount(getParentFragmentManager());
final String profileUsername = OsmOAuth.getUsername(); final String profileUsername = OsmOAuth.getUsername();
final Bitmap profilePicture = OsmOAuth.getProfilePicture(); final Bitmap profilePicture = OsmOAuth.getProfilePicture();

View File

@@ -11,7 +11,7 @@ import androidx.core.view.ViewCompat;
import app.organicmaps.R; import app.organicmaps.R;
import app.organicmaps.base.BaseMwmToolbarFragment; import app.organicmaps.base.BaseMwmToolbarFragment;
import app.organicmaps.sdk.editor.Editor; import app.organicmaps.sdk.editor.Editor;
import app.organicmaps.util.UiUtils; import app.organicmaps.sdk.util.UiUtils;
import app.organicmaps.util.WindowInsetUtils.ScrollableContentInsetsListener; import app.organicmaps.util.WindowInsetUtils.ScrollableContentInsetsListener;
import com.google.android.material.textfield.TextInputEditText; import com.google.android.material.textfield.TextInputEditText;

View File

@@ -13,13 +13,12 @@ import androidx.appcompat.widget.SwitchCompat;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import app.organicmaps.R; import app.organicmaps.R;
import app.organicmaps.editor.data.TimeFormatUtils;
import app.organicmaps.sdk.editor.OpeningHours; import app.organicmaps.sdk.editor.OpeningHours;
import app.organicmaps.sdk.editor.data.HoursMinutes; import app.organicmaps.sdk.editor.data.HoursMinutes;
import app.organicmaps.editor.data.TimeFormatUtils;
import app.organicmaps.sdk.editor.data.Timespan; import app.organicmaps.sdk.editor.data.Timespan;
import app.organicmaps.sdk.editor.data.Timetable; import app.organicmaps.sdk.editor.data.Timetable;
import app.organicmaps.sdk.util.Utils; import app.organicmaps.sdk.util.UiUtils;
import app.organicmaps.util.UiUtils;
import com.google.android.material.button.MaterialButton; import com.google.android.material.button.MaterialButton;
import com.google.android.material.checkbox.MaterialCheckBox; import com.google.android.material.checkbox.MaterialCheckBox;
import com.google.android.material.textview.MaterialTextView; import com.google.android.material.textview.MaterialTextView;
@@ -225,7 +224,7 @@ class SimpleTimetableAdapter extends RecyclerView.Adapter<SimpleTimetableAdapter
LayoutInflater.from(itemView.getContext()).inflate(R.layout.item_timetable_closed_hours, closedHost, false); LayoutInflater.from(itemView.getContext()).inflate(R.layout.item_timetable_closed_hours, closedHost, false);
closedHost.addView( closedHost.addView(
span, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, span, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
Utils.dimen(closedHost.getContext(), R.dimen.editor_height_closed))); UiUtils.dimen(closedHost.getContext(), R.dimen.editor_height_closed)));
closedHours[i] = span; closedHours[i] = span;
final int finalI = i; final int finalI = i;
span.findViewById(R.id.iv__remove_closed) span.findViewById(R.id.iv__remove_closed)

View File

@@ -10,7 +10,7 @@ import app.organicmaps.MwmApplication;
import app.organicmaps.R; import app.organicmaps.R;
import app.organicmaps.dialog.EditTextDialogFragment; import app.organicmaps.dialog.EditTextDialogFragment;
import app.organicmaps.sdk.editor.data.LocalizedStreet; import app.organicmaps.sdk.editor.data.LocalizedStreet;
import app.organicmaps.util.UiUtils; import app.organicmaps.sdk.util.UiUtils;
import com.google.android.material.radiobutton.MaterialRadioButton; import com.google.android.material.radiobutton.MaterialRadioButton;
import com.google.android.material.textview.MaterialTextView; import com.google.android.material.textview.MaterialTextView;

View File

@@ -15,7 +15,7 @@ import androidx.fragment.app.FragmentActivity;
import app.organicmaps.R; import app.organicmaps.R;
import app.organicmaps.base.BaseMwmFragment; import app.organicmaps.base.BaseMwmFragment;
import app.organicmaps.sdk.editor.OpeningHours; import app.organicmaps.sdk.editor.OpeningHours;
import app.organicmaps.util.UiUtils; import app.organicmaps.sdk.util.UiUtils;
import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.textview.MaterialTextView; import com.google.android.material.textview.MaterialTextView;
@@ -162,8 +162,8 @@ public class TimetableContainerFragment extends BaseMwmFragment implements Timet
switch (mMode) switch (mMode)
{ {
case SIMPLE -> setMode(Mode.ADVANCED, filledTimetables); case SIMPLE -> setMode(Mode.ADVANCED, filledTimetables);
case ADVANCED -> setMode(Mode.SIMPLE, filledTimetables); case ADVANCED -> setMode(Mode.SIMPLE, filledTimetables);
} }
} }

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