WIP: [Android] Speed limit exceed check improvements #9167
|
@ -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;DZZ)V");
|
||||
"[Lapp/organicmaps/routing/SingleLaneInfo;"
|
||||
"Lapp/organicmaps/util/SpeedFormatted;"
|
||||
"Lapp/organicmaps/util/SpeedFormatted;ZZ)V");
|
||||
|
||||
vector<routing::FollowingInfo::SingleLaneInfoClient> const & lanes = info.m_lanes;
|
||||
jobjectArray jLanes = nullptr;
|
||||
|
@ -1287,6 +1291,7 @@ Java_app_organicmaps_Framework_nativeGetRouteFollowingInfo(JNIEnv * env, jclass)
|
|||
}
|
||||
|
||||
auto const & rm = frm()->GetRoutingManager();
|
||||
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(
|
||||
|
@ -1294,8 +1299,9 @@ 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<jboolean>(isSpeedCamLimitExceeded),
|
||||
static_cast<jboolean>(shouldPlaySignal));
|
||||
info.m_time, jLanes, ToJavaSpeedFormatted(env, platform::SpeedFormatted(speed)),
|
||||
ToJavaSpeedFormatted(env, platform::SpeedFormatted(info.m_speedLimitMps)),
|
||||
static_cast<jboolean>(isSpeedCamLimitExceeded), static_cast<jboolean>(shouldPlaySignal));
|
||||
ASSERT(result, (jni::DescribeException()));
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -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<uint8_t>(speedFormatted.GetUnits()));
|
||||
|
||||
|
||||
return distanceObject;
|
||||
}
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
||||
strump
commented
I left these I left these `TODO` comments to make changes later. Too many changes for a single PR.
|
||||
|
|
|
@ -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,7 +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 SpeedFormatted speed;
|
||||
public final SpeedFormatted speedLimit;
|
||||
private final boolean speedCamLimitExceeded;
|
||||
private final boolean shouldPlayWarningSignal;
|
||||
|
||||
|
@ -143,8 +145,8 @@ 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)
|
||||
int totalTime, SingleLaneInfo[] lanes, SpeedFormatted speed, SpeedFormatted speedLimit,
|
||||
boolean speedCamLimitExceeded, boolean shouldPlayWarningSignal)
|
||||
{
|
||||
this.distToTarget = distToTarget;
|
||||
this.distToTurn = distToTurn;
|
||||
|
@ -158,8 +160,9 @@ public class RoutingInfo
|
|||
this.lanes = lanes;
|
||||
this.exitNum = exitNum;
|
||||
this.pedestrianTurnDirection = PedestrianTurnDirection.values()[pedestrianTurnOrdinal];
|
||||
this.speedLimitMps = speedLimitMps;
|
||||
this.speedCamLimitExceeded = speedLimitExceeded;
|
||||
this.speed = speed;
|
||||
this.speedLimit = speedLimit;
|
||||
this.speedCamLimitExceeded = speedCamLimitExceeded;
|
||||
this.shouldPlayWarningSignal = shouldPlayWarningSignal;
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
![]() nit: Are there simpler ideas on the implementation? E.g. return an array of 3 strings instead of introducing a class? How will it work on iOS? On Qt/desktop platforms? nit: Are there simpler ideas on the implementation? E.g. return an array of 3 strings instead of introducing a class? How will it work on iOS? On Qt/desktop platforms?
strump
commented
In iOS code navigation UI get speed from iOS API and speed limit from DTO To avoid JNI conversion from m/s to metric or imperial units I pass current speed and speed limit using You propose to pass In iOS code navigation UI get speed from iOS API and speed limit from DTO `MWMNavigationDashboardEntity` (see [NavigationControlView.swift:159](https://github.com/organicmaps/organicmaps/blob/master/iphone/Maps/Classes/CustomViews/NavigationDashboard/Views/NavigationControlView.swift#L159) ). iOS converts m/s to km/h or mi/h in UI code calling `measurement_utils::MpsToUnits(...)`. Also it compares speed and limit values in m/s and doesn't do rounding.
To avoid JNI conversion from m/s to metric or imperial units I pass current speed and speed limit using `SpeedFormatted` class. So we have only one JNI call to `Framework.nativeGetRouteFollowingInfo()`.
You propose to pass `String speedStr`, `String speedLimitStr`, `byte speedUnits`, `bool isSpeedLimitExceeded` instead, right?
![]() On iOS information comes from two methods:
It would be great to have one source of truth on all platforms, without any logic. E.g. just strings to display and flags like "speed limit was hit", or better 3-state like "no speeding/limit hit/fine limit hit". To avoid slow JNI calls, something like this may be used: Then it can be decoded in Java right before displaying. Passing all values separately is also ok, just slower a bit. On iOS information comes from two methods:
```
- (void)updateFollowingInfo:(routing::FollowingInfo const &)info routePoints:(NSArray<MWMRoutePoint *> *)points type:(MWMRouterType)type
and
- (void)updateTransitInfo:(TransitRouteInfo const &)info
```
It would be great to have one source of truth on all platforms, without any logic. E.g. just strings to display and flags like "speed limit was hit", or better 3-state like "no speeding/limit hit/fine limit hit".
To avoid slow JNI calls, something like this may be used:
`return currentSpeedStr.append(';').append(speedLimit).append(';').append(speedingFlag).apend(';').append(unitsStr);`
Then it can be decoded in Java right before displaying.
Passing all values separately is also ok, just slower a bit.
strump
commented
As far as I can tell, iOS doesn't have problem with units and formatting. Swift calls C++ function from New approach when I propose to drop As far as I can tell, iOS doesn't have problem with units and formatting. Swift calls C++ function from `measurement_utils` namespace to convert m/s to units-per-hour and format speed to string. No problems with Swift<->C++ calls, as far as I can see.
New approach when `MWMRoutingManager` for iOS and `Framework.nativeGetRouteFollowingInfo()` for Android do speed convertions and have some new protocol with `';'` separators looks like overengineering to me.
I propose to drop `SpeedFormatted` code and let UI do units conversions and string formatting with plain Java. I think functions within `measurement_utils` namespace would be easy to port to Java. This would minimize objects allocation between C++ and Java (get back `double speedLimitMps` as in `master`). What does iOS devs think of this?
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
|
@ -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<String, String> nativeFormatSpeedAndUnits(double metersPerSecond);
|
||||
public static native Distance nativeFormatDistance(double meters);
|
||||
@NonNull
|
||||
public static native Pair<String, String> nativeGetLocalizedDistanceUnits();
|
||||
|
|
|
@ -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<String, String> speedAndUnits = StringUtils.nativeFormatSpeedAndUnits(last.getSpeed());
|
||||
|
||||
if (info.speedLimitMps > 0.0)
|
||||
if (info.speedLimit != null && info.speedLimit.isValid())
|
||||
{
|
||||
Pair<String, String> speedLimitAndUnits = StringUtils.nativeFormatSpeedAndUnits(info.speedLimitMps);
|
||||
mSpeedValue.setText(speedAndUnits.first + "\u202F/\u202F" + speedLimitAndUnits.first);
|
||||
SpeedFormatted speedLimit = info.speedLimit;
|
||||
mSpeedValue.setText(speed.mSpeedStr + "\u202F/\u202F" + speedLimit.mSpeedStr);
|
||||
|
||||
speedLimitExceeded = (int)speed.mSpeed > (int)speedLimit.mSpeed;
|
||||
}
|
||||
else
|
||||
mSpeedValue.setText(speedAndUnits.first);
|
||||
mSpeedValue.setText(speed.mSpeedStr);
|
||||
|
||||
if (info.speedLimitMps > 0.0 && last.getSpeed() > info.speedLimitMps)
|
||||
if (speedLimitExceeded)
|
||||
strump
commented
No more No more `Pair<>` classes. `SpeedFormatted` contains all details for UI.
|
||||
{
|
||||
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());
|
||||
strump
commented
Here we decide if there was speeding Here we decide if there was speeding
![]() Here we compare km with km, or mi with mi, right? What about the threshold idea? Here we compare km with km, or mi with mi, right? What about the threshold idea?
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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; }
|
||||
|
|
68
platform/speed_formatted.cpp
Normal file
|
@ -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()) {}
|
||||
![]() It would be great to avoid introducing an implicit call to some external GetMeasurementUnits() method, and always pass it explicitly into the second constructor below. It would be great to avoid introducing an implicit call to some external GetMeasurementUnits() method, and always pass it explicitly into the second constructor below.
|
||||
|
||||
SpeedFormatted::SpeedFormatted(double speedMps, Units units) : m_units(units)
|
||||
{
|
||||
switch (units)
|
||||
{
|
||||
case Units::Metric:
|
||||
m_speed = speedMps < 0 ? speedMps : MpsToKmph(speedMps);
|
||||
break;
|
||||
case Units::Imperial:
|
||||
m_speed = speedMps < 0 ? speedMps : 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())
|
||||
![]() What is the situation when this class can be invalid? Can we guarantee that when it is created, it is always valid? What is the situation when this class can be invalid? Can we guarantee that when it is created, it is always valid?
strump
commented
Speed limit could be Speed limit could be `0.0` which means no limits at all. But if speed limit in OSM data is not set we have `-1.0`. So it makes sense to return `""` if OSM data has no limit defined.
![]() Store "invalid" value in valid class doesn't look clean. C++ has std::optional for that, there are also nullptr and empty string that can be used in JNI context to define "missing" speed limit vs "unlimited". AFAIR unlimited roads are only in Germany, and passing there something like 999 should work fine too. Store "invalid" value in valid class doesn't look clean. C++ has std::optional for that, there are also nullptr and empty string that can be used in JNI context to define "missing" speed limit vs "unlimited". AFAIR unlimited roads are only in Germany, and passing there something like 999 should work fine too.
|
||||
return "";
|
||||
![]() nit: return {} nit: 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
|
||||
![]() nit: We have in the UI:
There are already several classes for formatting. Does it make sense to write a single wrapper with simple interface for all these cases? nit: We have in the UI:
1. Distance
2. Speed
3. Time
4. Elevation
There are already several classes for formatting. Does it make sense to write a single wrapper with simple interface for all these cases?
|
||||
{
|
||||
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
|
32
platform/speed_formatted.hpp
Normal file
|
@ -0,0 +1,32 @@
|
|||
#pragma once
|
||||
|
||||
#include "platform/measurement_utils.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace platform
|
||||
{
|
||||
class SpeedFormatted
|
||||
![]() nit: Can it be a struct with 3 values, filled by a single function call? nit: Can it be a struct with 3 values, filled by a single function call?
|
||||
{
|
||||
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
|
|
@ -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))
|
||||
|
|
|
@ -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;
|
||||
|
|
nit: Looks like a lot of code, with a lot of jni overhead. Can formatting/string preparation be done all in C++ code, passed as several strings (or maybe just one string returned as a concatenation of several strings?) to the iOS and Android UI just for displaying?