From cd4e9d6b6c86d0dca61342453bbe105787ccc3e3 Mon Sep 17 00:00:00 2001 From: "S. Kozyr" Date: Fri, 23 Aug 2024 09:36:25 +0300 Subject: [PATCH 1/8] Check speed limit exceeded on the backend with respect to user locale. [Android] Pass 'isSpeedLimitExceeded' to RoutingInfo. Use this flag to mark speedometer red. Signed-off-by: S. Kozyr --- .../app/src/main/cpp/app/organicmaps/Framework.cpp | 8 +++++--- .../java/app/organicmaps/routing/RoutingInfo.java | 6 ++++-- .../java/app/organicmaps/widget/menu/NavMenu.java | 2 +- map/extrapolation/extrapolator.cpp | 5 +++++ map/extrapolation/extrapolator.hpp | 1 + map/framework.cpp | 5 +++++ map/framework.hpp | 1 + map/routing_manager.cpp | 5 +++++ map/routing_manager.hpp | 1 + routing/routing_session.cpp | 14 ++++++++++++++ routing/routing_session.hpp | 5 +++++ 11 files changed, 47 insertions(+), 6 deletions(-) diff --git a/android/app/src/main/cpp/app/organicmaps/Framework.cpp b/android/app/src/main/cpp/app/organicmaps/Framework.cpp index d29a67a69e..fcc1a940f0 100644 --- a/android/app/src/main/cpp/app/organicmaps/Framework.cpp +++ b/android/app/src/main/cpp/app/organicmaps/Framework.cpp @@ -1259,7 +1259,7 @@ Java_app_organicmaps_Framework_nativeGetRouteFollowingInfo(JNIEnv * env, jclass) jni::GetConstructorID(env, klass, "(Lapp/organicmaps/util/Distance;Lapp/organicmaps/util/Distance;" "Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;DIIIII" - "[Lapp/organicmaps/routing/SingleLaneInfo;DZZ)V"); + "[Lapp/organicmaps/routing/SingleLaneInfo;DZZZ)V"); vector const & lanes = info.m_lanes; jobjectArray jLanes = nullptr; @@ -1287,6 +1287,8 @@ Java_app_organicmaps_Framework_nativeGetRouteFollowingInfo(JNIEnv * env, jclass) } auto const & rm = frm()->GetRoutingManager(); + auto const * gpsInfo = fr->GetLastLocation(); + auto const isSpeedLimitExceeded = routing::RoutingSession::IsSpeedLimitExceeded(gpsInfo->m_speed, info.m_speedLimitMps, measurement_utils::GetMeasurementUnits()); auto const isSpeedCamLimitExceeded = rm.IsRoutingActive() ? rm.IsSpeedCamLimitExceeded() : false; auto const shouldPlaySignal = frm()->GetRoutingManager().GetSpeedCamManager().ShouldPlayBeepSignal(); jobject const result = env->NewObject( @@ -1294,8 +1296,8 @@ Java_app_organicmaps_Framework_nativeGetRouteFollowingInfo(JNIEnv * env, jclass) ToJavaDistance(env, info.m_distToTurn), jni::ToJavaString(env, info.m_currentStreetName), jni::ToJavaString(env, info.m_nextStreetName), jni::ToJavaString(env, info.m_nextNextStreetName), info.m_completionPercent, info.m_turn, info.m_nextTurn, info.m_pedestrianTurn, info.m_exitNum, - info.m_time, jLanes, info.m_speedLimitMps, static_cast(isSpeedCamLimitExceeded), - static_cast(shouldPlaySignal)); + info.m_time, jLanes, info.m_speedLimitMps, static_cast(isSpeedLimitExceeded), + static_cast(isSpeedCamLimitExceeded), static_cast(shouldPlaySignal)); ASSERT(result, (jni::DescribeException())); return result; } diff --git a/android/app/src/main/java/app/organicmaps/routing/RoutingInfo.java b/android/app/src/main/java/app/organicmaps/routing/RoutingInfo.java index 6b0a393783..6e439d6e31 100644 --- a/android/app/src/main/java/app/organicmaps/routing/RoutingInfo.java +++ b/android/app/src/main/java/app/organicmaps/routing/RoutingInfo.java @@ -37,6 +37,7 @@ public class RoutingInfo // Current speed limit in meters per second. // If no info about speed limit then speedLimitMps < 0. public final double speedLimitMps; + public final boolean speedLimitExceeded; private final boolean speedCamLimitExceeded; private final boolean shouldPlayWarningSignal; @@ -144,7 +145,7 @@ public class RoutingInfo public RoutingInfo(Distance distToTarget, Distance distToTurn, String currentStreet, String nextStreet, String nextNextStreet, double completionPercent, int vehicleTurnOrdinal, int vehicleNextTurnOrdinal, int pedestrianTurnOrdinal, int exitNum, int totalTime, SingleLaneInfo[] lanes, double speedLimitMps, boolean speedLimitExceeded, - boolean shouldPlayWarningSignal) + boolean speedCamLimitExceeded, boolean shouldPlayWarningSignal) { this.distToTarget = distToTarget; this.distToTurn = distToTurn; @@ -159,7 +160,8 @@ public class RoutingInfo this.exitNum = exitNum; this.pedestrianTurnDirection = PedestrianTurnDirection.values()[pedestrianTurnOrdinal]; this.speedLimitMps = speedLimitMps; - this.speedCamLimitExceeded = speedLimitExceeded; + this.speedLimitExceeded = speedLimitExceeded; + this.speedCamLimitExceeded = speedCamLimitExceeded; this.shouldPlayWarningSignal = shouldPlayWarningSignal; } diff --git a/android/app/src/main/java/app/organicmaps/widget/menu/NavMenu.java b/android/app/src/main/java/app/organicmaps/widget/menu/NavMenu.java index 763a3c86d1..cb46faab9f 100644 --- a/android/app/src/main/java/app/organicmaps/widget/menu/NavMenu.java +++ b/android/app/src/main/java/app/organicmaps/widget/menu/NavMenu.java @@ -222,7 +222,7 @@ public class NavMenu else mSpeedValue.setText(speedAndUnits.first); - if (info.speedLimitMps > 0.0 && last.getSpeed() > info.speedLimitMps) + if (info.speedLimitExceeded) { if (info.isSpeedCamLimitExceeded()) mSpeedValue.setTextColor(ContextCompat.getColor(mActivity, R.color.white_primary)); diff --git a/map/extrapolation/extrapolator.cpp b/map/extrapolation/extrapolator.cpp index 3f3fc2ada2..dc986fc1f4 100644 --- a/map/extrapolation/extrapolator.cpp +++ b/map/extrapolation/extrapolator.cpp @@ -149,6 +149,11 @@ void Extrapolator::OnLocationUpdate(location::GpsInfo const & gpsInfo) RunTaskOnBackgroundThread(false /* delayed */); } +location::GpsInfo const * Extrapolator::GetLastLocation() +{ + return &m_lastGpsInfo; +} + void Extrapolator::Enable(bool enabled) { lock_guard guard(m_mutex); diff --git a/map/extrapolation/extrapolator.hpp b/map/extrapolation/extrapolator.hpp index 4ae20fe2fb..2d59c3d06b 100644 --- a/map/extrapolation/extrapolator.hpp +++ b/map/extrapolation/extrapolator.hpp @@ -52,6 +52,7 @@ public: void OnLocationUpdate(location::GpsInfo const & gpsInfo); // @TODO(bykoianko) Gyroscope information should be taken into account as well for calculation // extrapolated position. + location::GpsInfo const * GetLastLocation(); void Enable(bool enabled); diff --git a/map/framework.cpp b/map/framework.cpp index 50a91c9e63..dcdc8eb596 100644 --- a/map/framework.cpp +++ b/map/framework.cpp @@ -190,6 +190,11 @@ void Framework::OnCompassUpdate(CompassInfo const & info) m_drapeEngine->SetCompassInfo(rInfo); } +GpsInfo const * Framework::GetLastLocation() +{ + return m_routingManager.GetLastLocation(); +} + void Framework::SwitchMyPositionNextMode() { if (m_drapeEngine != nullptr) diff --git a/map/framework.hpp b/map/framework.hpp index 42b15ee202..4a529e4d92 100644 --- a/map/framework.hpp +++ b/map/framework.hpp @@ -382,6 +382,7 @@ public: void OnLocationError(location::TLocationError error); void OnLocationUpdate(location::GpsInfo const & info); void OnCompassUpdate(location::CompassInfo const & info); + location::GpsInfo const * GetLastLocation(); void SwitchMyPositionNextMode(); /// Should be set before Drape initialization. Guarantees that fn is called in main thread context. void SetMyPositionModeListener(location::TMyPositionModeChanged && fn); diff --git a/map/routing_manager.cpp b/map/routing_manager.cpp index fab7dd21e1..426076c9af 100644 --- a/map/routing_manager.cpp +++ b/map/routing_manager.cpp @@ -472,6 +472,11 @@ void RoutingManager::OnLocationUpdate(location::GpsInfo const & info) m_extrapolator.OnLocationUpdate(info); } +location::GpsInfo const * RoutingManager::GetLastLocation() +{ + return m_extrapolator.GetLastLocation(); +} + RouterType RoutingManager::GetBestRouter(m2::PointD const & startPoint, m2::PointD const & finalPoint) const { diff --git a/map/routing_manager.hpp b/map/routing_manager.hpp index 0141d16ab0..ffa571ec36 100644 --- a/map/routing_manager.hpp +++ b/map/routing_manager.hpp @@ -252,6 +252,7 @@ public: void OnRemoveRoute(routing::RouterResultCode code); void OnRoutePointPassed(RouteMarkType type, size_t intermediateIndex); void OnLocationUpdate(location::GpsInfo const & info); + location::GpsInfo const * GetLastLocation(); routing::SpeedCameraManager & GetSpeedCamManager() { return m_routingSession.GetSpeedCamManager(); } bool IsSpeedCamLimitExceeded() const; diff --git a/routing/routing_session.cpp b/routing/routing_session.cpp index 89ea50a9de..9b989cd56f 100644 --- a/routing/routing_session.cpp +++ b/routing/routing_session.cpp @@ -465,6 +465,20 @@ double RoutingSession::GetCompletionPercent() const return percent; } +bool RoutingSession::IsSpeedLimitExceeded(double currentSpeedMps, double speedLimitMps, + const measurement_utils::Units units) +{ + if (currentSpeedMps <= 0 || speedLimitMps <= 0) + return false; + + // First convert meters/s to localized units + double currentSpeedLocal = measurement_utils::MpsToUnits(currentSpeedMps, units); + double speedLimitLocal = measurement_utils::MpsToUnits(speedLimitMps, units); + + // Floor speed and limit speed before comparison + return static_cast(currentSpeedLocal) > static_cast(speedLimitLocal); +} + void RoutingSession::PassCheckpoints() { CHECK_THREAD_CHECKER(m_threadChecker, ()); diff --git a/routing/routing_session.hpp b/routing/routing_session.hpp index 21041c24b1..dd9490cabd 100644 --- a/routing/routing_session.hpp +++ b/routing/routing_session.hpp @@ -174,6 +174,11 @@ public: double GetCompletionPercent() const; + // Convert speed and speed limit from meters/sec to miles/h or km/h. + // If current speed is the less or equal to max allowed speed then return false. + // We do comparison using floored speed values because on UI user see such values. + static bool IsSpeedLimitExceeded(double currentSpeedMps, double speedLimitMps, measurement_utils::Units units); + private: struct DoReadyCallback { -- 2.45.3 From e50ea2d492a90a39f7e11e28f9a2ca78cafe1b90 Mon Sep 17 00:00:00 2001 From: "S. Kozyr" Date: Thu, 5 Sep 2024 22:24:45 +0300 Subject: [PATCH 2/8] Introduced StringFormatted class into Framework and Java. Signed-off-by: S. Kozyr --- .../app/organicmaps/util/SpeedFormatted.hpp | 18 +++++ .../app/organicmaps/util/SpeedFormatted.java | 55 +++++++++++++++ platform/CMakeLists.txt | 2 + platform/measurement_utils.hpp | 1 + platform/speed_formatted.cpp | 68 +++++++++++++++++++ platform/speed_formatted.hpp | 32 +++++++++ 6 files changed, 176 insertions(+) create mode 100644 android/app/src/main/cpp/app/organicmaps/util/SpeedFormatted.hpp create mode 100644 android/app/src/main/java/app/organicmaps/util/SpeedFormatted.java create mode 100644 platform/speed_formatted.cpp create mode 100644 platform/speed_formatted.hpp diff --git a/android/app/src/main/cpp/app/organicmaps/util/SpeedFormatted.hpp b/android/app/src/main/cpp/app/organicmaps/util/SpeedFormatted.hpp new file mode 100644 index 0000000000..9e37d6e862 --- /dev/null +++ b/android/app/src/main/cpp/app/organicmaps/util/SpeedFormatted.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include "app/organicmaps/core/jni_helper.hpp" + +#include "platform/speed_formatted.hpp" + +inline jobject ToJavaSpeedFormatted(JNIEnv * env, platform::SpeedFormatted const & speedFormatted) +{ + static jclass const speedFormattedClass = jni::GetGlobalClassRef(env, "app/organicmaps/util/SpeedFormatted"); + + static jmethodID const speedFormattedConstructor = jni::GetConstructorID(env, speedFormattedClass, "(DLjava/lang/String;B)V"); + + jobject distanceObject = env->NewObject( + speedFormattedClass, speedFormattedConstructor, + speedFormatted.GetSpeed(), jni::ToJavaString(env, speedFormatted.GetSpeedString()), static_cast(speedFormatted.GetUnits())); + + return distanceObject; +} diff --git a/android/app/src/main/java/app/organicmaps/util/SpeedFormatted.java b/android/app/src/main/java/app/organicmaps/util/SpeedFormatted.java new file mode 100644 index 0000000000..a6c7043f29 --- /dev/null +++ b/android/app/src/main/java/app/organicmaps/util/SpeedFormatted.java @@ -0,0 +1,55 @@ +package app.organicmaps.util; + +import android.content.Context; + +import androidx.annotation.Keep; +import androidx.annotation.NonNull; +import androidx.annotation.StringRes; + +import app.organicmaps.R; + +// Used by JNI. +@Keep +@SuppressWarnings("unused") +public class SpeedFormatted +{ + /** + * IMPORTANT : Order of enum values MUST BE the same as native measurement_utils::Units enum. + */ + public enum Units + { + KilometersPerHour(R.string.kilometers_per_hour), + MilesPerHour(R.string.miles_per_hour); + + @StringRes + public final int mStringRes; + + Units(@StringRes int stringRes) + { + mStringRes = stringRes; + } + } + + public final double mSpeed; + @NonNull + public final String mSpeedStr; + public final SpeedFormatted.Units mUnits; + + public SpeedFormatted(double mSpeed, @NonNull String mSpeedStr, byte unitsIndex) + { + this.mSpeed = mSpeed; + this.mSpeedStr = mSpeedStr; + this.mUnits = Units.values()[unitsIndex]; + } + + public boolean isValid() + { + return mSpeed >= 0.0; + } + + @NonNull + public String getUnitsStr(@NonNull final Context context) + { + return context.getString(mUnits.mStringRes); + } +} diff --git a/platform/CMakeLists.txt b/platform/CMakeLists.txt index 1f6482e547..747d759e96 100644 --- a/platform/CMakeLists.txt +++ b/platform/CMakeLists.txt @@ -56,6 +56,8 @@ set(SRC socket.hpp string_storage_base.cpp string_storage_base.hpp + speed_formatted.cpp + speed_formatted.hpp utm_mgrs_utils.cpp utm_mgrs_utils.hpp ) diff --git a/platform/measurement_utils.hpp b/platform/measurement_utils.hpp index 906d4960e5..a518bba4bf 100644 --- a/platform/measurement_utils.hpp +++ b/platform/measurement_utils.hpp @@ -23,6 +23,7 @@ inline double MilesToFeet(double mi) { return mi * 5280.0; } inline double MiphToKmph(double miph) { return MilesToMeters(miph) / 1000.0; } inline double KmphToMiph(double kmph) { return MetersToMiles(kmph * 1000.0); } inline double MpsToKmph(double mps) { return mps * 3.6; } +inline double MpsToMiph(double mps) { return mps * 3.6 * 0.621371192; } inline double MetersToFeet(double m) { return m * 3.2808399; } inline double FeetToMeters(double ft) { return ft * 0.3048; } inline double FeetToMiles(double ft) { return ft * 0.00018939; } diff --git a/platform/speed_formatted.cpp b/platform/speed_formatted.cpp new file mode 100644 index 0000000000..142b55a012 --- /dev/null +++ b/platform/speed_formatted.cpp @@ -0,0 +1,68 @@ +#include "speed_formatted.hpp" + +#include "platform/locale.hpp" +#include "platform/localization.hpp" +#include "platform/measurement_utils.hpp" + +#include "base/assert.hpp" + +namespace platform +{ +using namespace measurement_utils; + +SpeedFormatted::SpeedFormatted(double speedMps) : SpeedFormatted(speedMps, GetMeasurementUnits()) {} + +SpeedFormatted::SpeedFormatted(double speedMps, Units units) : m_units(units) +{ + switch (units) + { + case Units::Metric: + m_speed = MpsToKmph(speedMps); + break; + case Units::Imperial: + m_speed = MpsToMiph(speedMps); + break; + default: UNREACHABLE(); + } +} + +bool SpeedFormatted::IsValid() const { return m_speed >= 0.0; } + +double SpeedFormatted::GetSpeed() const { return m_speed; } + +Units SpeedFormatted::GetUnits() const { return m_units; } + +std::string SpeedFormatted::GetSpeedString() const +{ + if (!IsValid()) + return ""; + + // Default precision is 0 (no decimals). + int precision = 0; + + // Set 1 decimal precision for speed per hour (km/h, miles/h) lower than 10.0 (9.5, 7.0,...). + if (m_speed < 10.0) + precision = 1; + + return ToStringPrecision(m_speed, precision); +} + +std::string SpeedFormatted::GetUnitsString() const +{ + switch (m_units) + { + case Units::Metric: return GetLocalizedString("kilometers_per_hour"); + case Units::Imperial: return GetLocalizedString("miles_per_hour"); + default: UNREACHABLE(); + } +} + +std::string SpeedFormatted::ToString() const +{ + if (!IsValid()) + return ""; + + return GetSpeedString() + kNarrowNonBreakingSpace + GetUnitsString(); +} + +} // namespace platform diff --git a/platform/speed_formatted.hpp b/platform/speed_formatted.hpp new file mode 100644 index 0000000000..6cbcb58fa0 --- /dev/null +++ b/platform/speed_formatted.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include "platform/measurement_utils.hpp" + +#include + +namespace platform +{ +class SpeedFormatted +{ +public: + SpeedFormatted(double speedInMps); // Initialize with m/s value and default measurement units + SpeedFormatted(double speedInMps, measurement_utils::Units units); + + bool IsValid() const; + + double GetSpeed() const; + measurement_utils::Units GetUnits() const; + + std::string GetSpeedString() const; + std::string GetUnitsString() const; + + std::string ToString() const; + + friend std::string DebugPrint(SpeedFormatted const & d) { return d.ToString(); } + +private: + double m_speed; // Speed in km/h or mile/h depending on m_units. + measurement_utils::Units m_units; +}; + +} // namespace platform -- 2.45.3 From 9b1fa93b370487e3991a0b00f33905f0c29a3128 Mon Sep 17 00:00:00 2001 From: "S. Kozyr" Date: Thu, 5 Sep 2024 22:26:39 +0300 Subject: [PATCH 3/8] [Android] Added SpeedFormatted fields to RoutingInfo class. Changed Framework to pass those values to routing panel code. Signed-off-by: S. Kozyr --- .../main/cpp/app/organicmaps/Framework.cpp | 10 ++++++--- .../app/organicmaps/routing/RoutingInfo.java | 11 +++++----- .../app/organicmaps/widget/menu/NavMenu.java | 22 +++++++++---------- 3 files changed, 24 insertions(+), 19 deletions(-) diff --git a/android/app/src/main/cpp/app/organicmaps/Framework.cpp b/android/app/src/main/cpp/app/organicmaps/Framework.cpp index fcc1a940f0..18931d7777 100644 --- a/android/app/src/main/cpp/app/organicmaps/Framework.cpp +++ b/android/app/src/main/cpp/app/organicmaps/Framework.cpp @@ -7,6 +7,7 @@ #include "app/organicmaps/util/Distance.hpp" #include "app/organicmaps/util/FeatureIdBuilder.hpp" #include "app/organicmaps/util/NetworkPolicy.hpp" +#include "app/organicmaps/util/SpeedFormatted.hpp" #include "app/organicmaps/vulkan/android_vulkan_context_factory.hpp" #include "map/bookmark_helpers.hpp" @@ -48,6 +49,7 @@ #include "platform/platform.hpp" #include "platform/preferred_languages.hpp" #include "platform/settings.hpp" +#include "platform/speed_formatted.hpp" #include "platform/utm_mgrs_utils.hpp" #include "base/assert.hpp" @@ -1259,7 +1261,9 @@ Java_app_organicmaps_Framework_nativeGetRouteFollowingInfo(JNIEnv * env, jclass) jni::GetConstructorID(env, klass, "(Lapp/organicmaps/util/Distance;Lapp/organicmaps/util/Distance;" "Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;DIIIII" - "[Lapp/organicmaps/routing/SingleLaneInfo;DZZZ)V"); + "[Lapp/organicmaps/routing/SingleLaneInfo;" + "Lapp/organicmaps/util/SpeedFormatted;" + "Lapp/organicmaps/util/SpeedFormatted;ZZ)V"); vector const & lanes = info.m_lanes; jobjectArray jLanes = nullptr; @@ -1288,7 +1292,6 @@ Java_app_organicmaps_Framework_nativeGetRouteFollowingInfo(JNIEnv * env, jclass) auto const & rm = frm()->GetRoutingManager(); auto const * gpsInfo = fr->GetLastLocation(); - auto const isSpeedLimitExceeded = routing::RoutingSession::IsSpeedLimitExceeded(gpsInfo->m_speed, info.m_speedLimitMps, measurement_utils::GetMeasurementUnits()); auto const isSpeedCamLimitExceeded = rm.IsRoutingActive() ? rm.IsSpeedCamLimitExceeded() : false; auto const shouldPlaySignal = frm()->GetRoutingManager().GetSpeedCamManager().ShouldPlayBeepSignal(); jobject const result = env->NewObject( @@ -1296,7 +1299,8 @@ Java_app_organicmaps_Framework_nativeGetRouteFollowingInfo(JNIEnv * env, jclass) ToJavaDistance(env, info.m_distToTurn), jni::ToJavaString(env, info.m_currentStreetName), jni::ToJavaString(env, info.m_nextStreetName), jni::ToJavaString(env, info.m_nextNextStreetName), info.m_completionPercent, info.m_turn, info.m_nextTurn, info.m_pedestrianTurn, info.m_exitNum, - info.m_time, jLanes, info.m_speedLimitMps, static_cast(isSpeedLimitExceeded), + info.m_time, jLanes, ToJavaSpeedFormatted(env, platform::SpeedFormatted(gpsInfo->m_speed)), + ToJavaSpeedFormatted(env, platform::SpeedFormatted(info.m_speedLimitMps)), static_cast(isSpeedCamLimitExceeded), static_cast(shouldPlaySignal)); ASSERT(result, (jni::DescribeException())); return result; diff --git a/android/app/src/main/java/app/organicmaps/routing/RoutingInfo.java b/android/app/src/main/java/app/organicmaps/routing/RoutingInfo.java index 6e439d6e31..73d85c267d 100644 --- a/android/app/src/main/java/app/organicmaps/routing/RoutingInfo.java +++ b/android/app/src/main/java/app/organicmaps/routing/RoutingInfo.java @@ -8,6 +8,7 @@ import androidx.annotation.NonNull; import app.organicmaps.R; import app.organicmaps.util.Distance; +import app.organicmaps.util.SpeedFormatted; // Called from JNI. @Keep @@ -36,8 +37,8 @@ public class RoutingInfo public final PedestrianTurnDirection pedestrianTurnDirection; // Current speed limit in meters per second. // If no info about speed limit then speedLimitMps < 0. - public final double speedLimitMps; - public final boolean speedLimitExceeded; + public final SpeedFormatted speed; + public final SpeedFormatted speedLimit; private final boolean speedCamLimitExceeded; private final boolean shouldPlayWarningSignal; @@ -144,7 +145,7 @@ public class RoutingInfo public RoutingInfo(Distance distToTarget, Distance distToTurn, String currentStreet, String nextStreet, String nextNextStreet, double completionPercent, int vehicleTurnOrdinal, int vehicleNextTurnOrdinal, int pedestrianTurnOrdinal, int exitNum, - int totalTime, SingleLaneInfo[] lanes, double speedLimitMps, boolean speedLimitExceeded, + int totalTime, SingleLaneInfo[] lanes, SpeedFormatted speed, SpeedFormatted speedLimit, boolean speedCamLimitExceeded, boolean shouldPlayWarningSignal) { this.distToTarget = distToTarget; @@ -159,8 +160,8 @@ public class RoutingInfo this.lanes = lanes; this.exitNum = exitNum; this.pedestrianTurnDirection = PedestrianTurnDirection.values()[pedestrianTurnOrdinal]; - this.speedLimitMps = speedLimitMps; - this.speedLimitExceeded = speedLimitExceeded; + this.speed = speed; + this.speedLimit = speedLimit; this.speedCamLimitExceeded = speedCamLimitExceeded; this.shouldPlayWarningSignal = shouldPlayWarningSignal; } diff --git a/android/app/src/main/java/app/organicmaps/widget/menu/NavMenu.java b/android/app/src/main/java/app/organicmaps/widget/menu/NavMenu.java index cb46faab9f..af7af7205d 100644 --- a/android/app/src/main/java/app/organicmaps/widget/menu/NavMenu.java +++ b/android/app/src/main/java/app/organicmaps/widget/menu/NavMenu.java @@ -17,6 +17,7 @@ import app.organicmaps.location.LocationHelper; import app.organicmaps.routing.RoutingInfo; import app.organicmaps.sound.TtsPlayer; import app.organicmaps.util.Graphics; +import app.organicmaps.util.SpeedFormatted; import app.organicmaps.util.StringUtils; import app.organicmaps.util.ThemeUtils; import app.organicmaps.util.UiUtils; @@ -208,21 +209,20 @@ public class NavMenu private void updateSpeedView(@NonNull RoutingInfo info) { - final Location last = LocationHelper.from(mActivity).getSavedLocation(); - if (last == null) - return; + SpeedFormatted speed = info.speed; + boolean speedLimitExceeded = false; - Pair speedAndUnits = StringUtils.nativeFormatSpeedAndUnits(last.getSpeed()); - - if (info.speedLimitMps > 0.0) + if (info.speedLimit != null && info.speedLimit.isValid()) { - Pair speedLimitAndUnits = StringUtils.nativeFormatSpeedAndUnits(info.speedLimitMps); - mSpeedValue.setText(speedAndUnits.first + "\u202F/\u202F" + speedLimitAndUnits.first); + SpeedFormatted speedLimit = info.speedLimit; + mSpeedValue.setText(speed.mSpeedStr + "\u202F/\u202F" + speedLimit.getUnitsStr(mActivity)); + + speedLimitExceeded = (int)speed.mSpeed > (int)speedLimit.mSpeed; } else - mSpeedValue.setText(speedAndUnits.first); + mSpeedValue.setText(speed.mSpeedStr); - if (info.speedLimitExceeded) + if (speedLimitExceeded) { if (info.isSpeedCamLimitExceeded()) mSpeedValue.setTextColor(ContextCompat.getColor(mActivity, R.color.white_primary)); @@ -232,7 +232,7 @@ public class NavMenu else mSpeedValue.setTextColor(ThemeUtils.getColor(mActivity, android.R.attr.textColorPrimary)); - mSpeedUnits.setText(speedAndUnits.second); + mSpeedUnits.setText(info.speed.getUnitsStr(mActivity)); mSpeedViewContainer.setActivated(info.isSpeedCamLimitExceeded()); } -- 2.45.3 From eef4ff08fad2499887152af6047b666189ce0c31 Mon Sep 17 00:00:00 2001 From: "S. Kozyr" Date: Thu, 5 Sep 2024 22:26:53 +0300 Subject: [PATCH 4/8] Removed unused code Signed-off-by: S. Kozyr --- .../main/cpp/app/organicmaps/util/StringUtils.cpp | 8 -------- .../java/app/organicmaps/util/StringUtils.java | 1 - routing/routing_session.cpp | 14 -------------- routing/routing_session.hpp | 5 ----- 4 files changed, 28 deletions(-) diff --git a/android/app/src/main/cpp/app/organicmaps/util/StringUtils.cpp b/android/app/src/main/cpp/app/organicmaps/util/StringUtils.cpp index e99a34b0ed..a4009d316f 100644 --- a/android/app/src/main/cpp/app/organicmaps/util/StringUtils.cpp +++ b/android/app/src/main/cpp/app/organicmaps/util/StringUtils.cpp @@ -49,14 +49,6 @@ Java_app_organicmaps_util_StringUtils_nativeFilterContainsNormalized(JNIEnv * en return jni::ToJavaStringArray(env, filtered); } -JNIEXPORT jobject JNICALL Java_app_organicmaps_util_StringUtils_nativeFormatSpeedAndUnits( - JNIEnv * env, jclass thiz, jdouble metersPerSecond) -{ - auto const units = measurement_utils::GetMeasurementUnits(); - return MakeJavaPair(env, measurement_utils::FormatSpeedNumeric(metersPerSecond, units), - platform::GetLocalizedSpeedUnits(units)); -} - JNIEXPORT jobject JNICALL Java_app_organicmaps_util_StringUtils_nativeFormatDistance(JNIEnv * env, jclass, jdouble distanceInMeters) { diff --git a/android/app/src/main/java/app/organicmaps/util/StringUtils.java b/android/app/src/main/java/app/organicmaps/util/StringUtils.java index d82b80a092..d503ccb1b3 100644 --- a/android/app/src/main/java/app/organicmaps/util/StringUtils.java +++ b/android/app/src/main/java/app/organicmaps/util/StringUtils.java @@ -29,7 +29,6 @@ public class StringUtils public static native boolean nativeContainsNormalized(String str, String substr); public static native String[] nativeFilterContainsNormalized(String[] strings, String substr); - public static native Pair nativeFormatSpeedAndUnits(double metersPerSecond); public static native Distance nativeFormatDistance(double meters); @NonNull public static native Pair nativeGetLocalizedDistanceUnits(); diff --git a/routing/routing_session.cpp b/routing/routing_session.cpp index 9b989cd56f..89ea50a9de 100644 --- a/routing/routing_session.cpp +++ b/routing/routing_session.cpp @@ -465,20 +465,6 @@ double RoutingSession::GetCompletionPercent() const return percent; } -bool RoutingSession::IsSpeedLimitExceeded(double currentSpeedMps, double speedLimitMps, - const measurement_utils::Units units) -{ - if (currentSpeedMps <= 0 || speedLimitMps <= 0) - return false; - - // First convert meters/s to localized units - double currentSpeedLocal = measurement_utils::MpsToUnits(currentSpeedMps, units); - double speedLimitLocal = measurement_utils::MpsToUnits(speedLimitMps, units); - - // Floor speed and limit speed before comparison - return static_cast(currentSpeedLocal) > static_cast(speedLimitLocal); -} - void RoutingSession::PassCheckpoints() { CHECK_THREAD_CHECKER(m_threadChecker, ()); diff --git a/routing/routing_session.hpp b/routing/routing_session.hpp index dd9490cabd..21041c24b1 100644 --- a/routing/routing_session.hpp +++ b/routing/routing_session.hpp @@ -174,11 +174,6 @@ public: double GetCompletionPercent() const; - // Convert speed and speed limit from meters/sec to miles/h or km/h. - // If current speed is the less or equal to max allowed speed then return false. - // We do comparison using floored speed values because on UI user see such values. - static bool IsSpeedLimitExceeded(double currentSpeedMps, double speedLimitMps, measurement_utils::Units units); - private: struct DoReadyCallback { -- 2.45.3 From 07626f8e36e076859fa82a4c9aeb4cf1e11e883c Mon Sep 17 00:00:00 2001 From: "S. Kozyr" Date: Thu, 5 Sep 2024 22:27:09 +0300 Subject: [PATCH 5/8] [Android] Added some TODOs Signed-off-by: S. Kozyr --- android/app/src/main/java/app/organicmaps/Framework.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/android/app/src/main/java/app/organicmaps/Framework.java b/android/app/src/main/java/app/organicmaps/Framework.java index b99bcaf85c..0e56f0b5be 100644 --- a/android/app/src/main/java/app/organicmaps/Framework.java +++ b/android/app/src/main/java/app/organicmaps/Framework.java @@ -187,11 +187,11 @@ public class Framework public static native DistanceAndAzimut nativeGetDistanceAndAzimuthFromLatLon(double dstLat, double dstLon, double srcLat, double srcLon, double north); - public static native String nativeFormatLatLon(double lat, double lon, int coordFormat); + public static native String nativeFormatLatLon(double lat, double lon, int coordFormat); // TODO: move this method to StringUtils class. - public static native String nativeFormatAltitude(double alt); + public static native String nativeFormatAltitude(double alt); // TODO: move this method to StringUtils class. - public static native String nativeFormatSpeed(double speed); + public static native String nativeFormatSpeed(double speed); // TODO: move this method to StringUtils class. public static native String nativeGetGe0Url(double lat, double lon, double zoomLevel, String name); -- 2.45.3 From 57847d597f4837898226215fa8c240364e40e11c Mon Sep 17 00:00:00 2001 From: "S. Kozyr" Date: Thu, 5 Sep 2024 22:32:18 +0300 Subject: [PATCH 6/8] Fixed negative (invalid) speed handling Signed-off-by: S. Kozyr --- platform/speed_formatted.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platform/speed_formatted.cpp b/platform/speed_formatted.cpp index 142b55a012..c0a9562022 100644 --- a/platform/speed_formatted.cpp +++ b/platform/speed_formatted.cpp @@ -17,10 +17,10 @@ SpeedFormatted::SpeedFormatted(double speedMps, Units units) : m_units(units) switch (units) { case Units::Metric: - m_speed = MpsToKmph(speedMps); + m_speed = speedMps < 0 ? speedMps : MpsToKmph(speedMps); break; case Units::Imperial: - m_speed = MpsToMiph(speedMps); + m_speed = speedMps < 0 ? speedMps : MpsToMiph(speedMps); break; default: UNREACHABLE(); } -- 2.45.3 From 4497de895fa481de114f27f02cd7321193fc02be Mon Sep 17 00:00:00 2001 From: "S. Kozyr" Date: Thu, 5 Sep 2024 22:48:31 +0300 Subject: [PATCH 7/8] Removed 'GetLastLocation' methods. Introduced RoutingSession.GetLastSpeed() Signed-off-by: S. Kozyr --- android/app/src/main/cpp/app/organicmaps/Framework.cpp | 4 ++-- map/extrapolation/extrapolator.cpp | 5 ----- map/extrapolation/extrapolator.hpp | 1 - map/framework.cpp | 5 ----- map/framework.hpp | 1 - map/routing_manager.cpp | 5 ----- map/routing_manager.hpp | 1 - routing/routing_session.cpp | 1 + routing/routing_session.hpp | 2 ++ 9 files changed, 5 insertions(+), 20 deletions(-) diff --git a/android/app/src/main/cpp/app/organicmaps/Framework.cpp b/android/app/src/main/cpp/app/organicmaps/Framework.cpp index 18931d7777..05a5114f4b 100644 --- a/android/app/src/main/cpp/app/organicmaps/Framework.cpp +++ b/android/app/src/main/cpp/app/organicmaps/Framework.cpp @@ -1291,7 +1291,7 @@ Java_app_organicmaps_Framework_nativeGetRouteFollowingInfo(JNIEnv * env, jclass) } auto const & rm = frm()->GetRoutingManager(); - auto const * gpsInfo = fr->GetLastLocation(); + auto const speed = rm.RoutingSession().GetLastSpeed(); auto const isSpeedCamLimitExceeded = rm.IsRoutingActive() ? rm.IsSpeedCamLimitExceeded() : false; auto const shouldPlaySignal = frm()->GetRoutingManager().GetSpeedCamManager().ShouldPlayBeepSignal(); jobject const result = env->NewObject( @@ -1299,7 +1299,7 @@ Java_app_organicmaps_Framework_nativeGetRouteFollowingInfo(JNIEnv * env, jclass) ToJavaDistance(env, info.m_distToTurn), jni::ToJavaString(env, info.m_currentStreetName), jni::ToJavaString(env, info.m_nextStreetName), jni::ToJavaString(env, info.m_nextNextStreetName), info.m_completionPercent, info.m_turn, info.m_nextTurn, info.m_pedestrianTurn, info.m_exitNum, - info.m_time, jLanes, ToJavaSpeedFormatted(env, platform::SpeedFormatted(gpsInfo->m_speed)), + info.m_time, jLanes, ToJavaSpeedFormatted(env, platform::SpeedFormatted(speed)), ToJavaSpeedFormatted(env, platform::SpeedFormatted(info.m_speedLimitMps)), static_cast(isSpeedCamLimitExceeded), static_cast(shouldPlaySignal)); ASSERT(result, (jni::DescribeException())); diff --git a/map/extrapolation/extrapolator.cpp b/map/extrapolation/extrapolator.cpp index dc986fc1f4..3f3fc2ada2 100644 --- a/map/extrapolation/extrapolator.cpp +++ b/map/extrapolation/extrapolator.cpp @@ -149,11 +149,6 @@ void Extrapolator::OnLocationUpdate(location::GpsInfo const & gpsInfo) RunTaskOnBackgroundThread(false /* delayed */); } -location::GpsInfo const * Extrapolator::GetLastLocation() -{ - return &m_lastGpsInfo; -} - void Extrapolator::Enable(bool enabled) { lock_guard guard(m_mutex); diff --git a/map/extrapolation/extrapolator.hpp b/map/extrapolation/extrapolator.hpp index 2d59c3d06b..4ae20fe2fb 100644 --- a/map/extrapolation/extrapolator.hpp +++ b/map/extrapolation/extrapolator.hpp @@ -52,7 +52,6 @@ public: void OnLocationUpdate(location::GpsInfo const & gpsInfo); // @TODO(bykoianko) Gyroscope information should be taken into account as well for calculation // extrapolated position. - location::GpsInfo const * GetLastLocation(); void Enable(bool enabled); diff --git a/map/framework.cpp b/map/framework.cpp index dcdc8eb596..50a91c9e63 100644 --- a/map/framework.cpp +++ b/map/framework.cpp @@ -190,11 +190,6 @@ void Framework::OnCompassUpdate(CompassInfo const & info) m_drapeEngine->SetCompassInfo(rInfo); } -GpsInfo const * Framework::GetLastLocation() -{ - return m_routingManager.GetLastLocation(); -} - void Framework::SwitchMyPositionNextMode() { if (m_drapeEngine != nullptr) diff --git a/map/framework.hpp b/map/framework.hpp index 4a529e4d92..42b15ee202 100644 --- a/map/framework.hpp +++ b/map/framework.hpp @@ -382,7 +382,6 @@ public: void OnLocationError(location::TLocationError error); void OnLocationUpdate(location::GpsInfo const & info); void OnCompassUpdate(location::CompassInfo const & info); - location::GpsInfo const * GetLastLocation(); void SwitchMyPositionNextMode(); /// Should be set before Drape initialization. Guarantees that fn is called in main thread context. void SetMyPositionModeListener(location::TMyPositionModeChanged && fn); diff --git a/map/routing_manager.cpp b/map/routing_manager.cpp index 426076c9af..fab7dd21e1 100644 --- a/map/routing_manager.cpp +++ b/map/routing_manager.cpp @@ -472,11 +472,6 @@ void RoutingManager::OnLocationUpdate(location::GpsInfo const & info) m_extrapolator.OnLocationUpdate(info); } -location::GpsInfo const * RoutingManager::GetLastLocation() -{ - return m_extrapolator.GetLastLocation(); -} - RouterType RoutingManager::GetBestRouter(m2::PointD const & startPoint, m2::PointD const & finalPoint) const { diff --git a/map/routing_manager.hpp b/map/routing_manager.hpp index ffa571ec36..0141d16ab0 100644 --- a/map/routing_manager.hpp +++ b/map/routing_manager.hpp @@ -252,7 +252,6 @@ public: void OnRemoveRoute(routing::RouterResultCode code); void OnRoutePointPassed(RouteMarkType type, size_t intermediateIndex); void OnLocationUpdate(location::GpsInfo const & info); - location::GpsInfo const * GetLastLocation(); routing::SpeedCameraManager & GetSpeedCamManager() { return m_routingSession.GetSpeedCamManager(); } bool IsSpeedCamLimitExceeded() const; diff --git a/routing/routing_session.cpp b/routing/routing_session.cpp index 89ea50a9de..f3204ada79 100644 --- a/routing/routing_session.cpp +++ b/routing/routing_session.cpp @@ -273,6 +273,7 @@ SessionState RoutingSession::OnLocationPositionChanged(GpsInfo const & info) return m_state; m_turnNotificationsMgr.SetSpeedMetersPerSecond(info.m_speed); + m_lastSpeed = info.m_speed; auto const formerIter = m_route->GetCurrentIteratorTurn(); if (m_route->MoveIterator(info)) diff --git a/routing/routing_session.hpp b/routing/routing_session.hpp index 21041c24b1..33b3a6323f 100644 --- a/routing/routing_session.hpp +++ b/routing/routing_session.hpp @@ -173,6 +173,7 @@ public: void SetGuidesForTests(GuidesTracks guides) { m_router->SetGuidesTracks(std::move(guides)); } double GetCompletionPercent() const; + double GetLastSpeed() const { return m_lastSpeed; } private: struct DoReadyCallback @@ -208,6 +209,7 @@ private: double m_lastDistance = 0.0; int m_moveAwayCounter = 0; m2::PointD m_lastGoodPosition; + double m_lastSpeed = -1.0; m2::PointD m_userCurrentPosition; bool m_userCurrentPositionValid = false; -- 2.45.3 From dd7cb529aa94f361f5b9357538e8ac481483e6d9 Mon Sep 17 00:00:00 2001 From: "S. Kozyr" Date: Sat, 7 Sep 2024 13:48:35 +0300 Subject: [PATCH 8/8] [Android] Fixed speed limit on UI Signed-off-by: S. Kozyr --- .../app/src/main/java/app/organicmaps/widget/menu/NavMenu.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/app/src/main/java/app/organicmaps/widget/menu/NavMenu.java b/android/app/src/main/java/app/organicmaps/widget/menu/NavMenu.java index af7af7205d..3b1ee9c90d 100644 --- a/android/app/src/main/java/app/organicmaps/widget/menu/NavMenu.java +++ b/android/app/src/main/java/app/organicmaps/widget/menu/NavMenu.java @@ -215,7 +215,7 @@ public class NavMenu if (info.speedLimit != null && info.speedLimit.isValid()) { SpeedFormatted speedLimit = info.speedLimit; - mSpeedValue.setText(speed.mSpeedStr + "\u202F/\u202F" + speedLimit.getUnitsStr(mActivity)); + mSpeedValue.setText(speed.mSpeedStr + "\u202F/\u202F" + speedLimit.mSpeedStr); speedLimitExceeded = (int)speed.mSpeed > (int)speedLimit.mSpeed; } -- 2.45.3