[android] Display distance and time to next stop

Signed-off-by: Gonzalo Pesquero <gpesquero@yahoo.es>
This commit is contained in:
Gonzalo Pesquero 2024-08-21 01:38:45 +02:00
parent 6991fe017a
commit 228dc7318b
11 changed files with 317 additions and 4 deletions

View file

@ -1254,12 +1254,16 @@ Java_app_organicmaps_Framework_nativeGetRouteFollowingInfo(JNIEnv * env, jclass)
// String currentStreet, String nextStreet, String nextNextStreet,
// double completionPercent, int vehicleTurnOrdinal, int
// vehicleNextTurnOrdinal, int pedestrianTurnOrdinal, int exitNum,
// int totalTime, SingleLaneInfo[] lanes)
// int totalTime, SingleLaneInfo[] lanes, double speedLimitMps,
// boolean speedLimitExceeded, boolean shouldPlayWarningSignal,
// int nextStopPos, Distance distToNextStop, int timeToNextStop)
static jmethodID const ctorRouteInfoID =
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;DZZ"
"ILapp/organicmaps/util/Distance;I)V");
vector<routing::FollowingInfo::SingleLaneInfoClient> const & lanes = info.m_lanes;
jobjectArray jLanes = nullptr;
@ -1289,13 +1293,15 @@ Java_app_organicmaps_Framework_nativeGetRouteFollowingInfo(JNIEnv * env, jclass)
auto const & rm = frm()->GetRoutingManager();
auto const isSpeedCamLimitExceeded = rm.IsRoutingActive() ? rm.IsSpeedCamLimitExceeded() : false;
auto const shouldPlaySignal = frm()->GetRoutingManager().GetSpeedCamManager().ShouldPlayBeepSignal();
jobject const result = env->NewObject(
klass, ctorRouteInfoID, ToJavaDistance(env, info.m_distToTarget),
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));
static_cast<jboolean>(shouldPlaySignal), info.m_nextStopPos,
ToJavaDistance(env, info.m_distToNextStop), info.m_timeToNextStop);
ASSERT(result, (jni::DescribeException()));
return result;
}

View file

@ -1,6 +1,7 @@
package app.organicmaps.routing;
import android.text.TextUtils;
import android.text.format.DateFormat;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
@ -19,7 +20,10 @@ import app.organicmaps.util.Utils;
import app.organicmaps.widget.menu.NavMenu;
import com.google.android.material.bottomsheet.BottomSheetBehavior;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
public class NavigationController implements TrafficManager.TrafficCallback,
NavMenu.NavMenuListener
@ -43,6 +47,12 @@ public class NavigationController implements TrafficManager.TrafficCallback,
@NonNull
private final LanesAdapter mLanesAdapter;
private final View mNextStopFrame;
private final TextView mNextStopPos;
private final TextView mNextStopDistance;
private final TextView mNextStopRemainingTime;
private final TextView mNextStopEta;
private final NavMenu mNavMenu;
View.OnClickListener mOnSettingsClickListener;
@ -88,6 +98,12 @@ public class NavigationController implements TrafficManager.TrafficCallback,
mLanesAdapter = new LanesAdapter();
initLanesRecycler();
mNextStopFrame = topFrame.findViewById(R.id.next_stop_frame);
mNextStopPos = topFrame.findViewById(R.id.next_stop_pos);
mNextStopDistance = topFrame.findViewById(R.id.distance_to_next_stop);
mNextStopRemainingTime = topFrame.findViewById(R.id.remaining_time_to_next_stop);
mNextStopEta = topFrame.findViewById(R.id.eta_to_next_stop);
// Show a blank view below the navbar to hide the menu content
final View navigationBarBackground = mFrame.findViewById(R.id.nav_bottom_sheet_nav_bar);
final View nextTurnContainer = mFrame.findViewById(R.id.nav_next_turn_container);
@ -155,6 +171,9 @@ public class NavigationController implements TrafficManager.TrafficCallback,
updateVehicle(info);
updateStreetView(info);
updateNextStopInfo(info);
mNavMenu.update(info);
}
@ -168,6 +187,41 @@ public class NavigationController implements TrafficManager.TrafficCallback,
mNextStreet.setText(info.nextStreet);
}
private void updateNextStopInfo(@NonNull RoutingInfo info)
{
if ((info.nextStopPos > 0) && (info.nextStopPos < 100))
{
// Show next stop info.
UiUtils.show(mNextStopFrame);
mNextStopPos.setText(String.valueOf(info.nextStopPos));
mNextStopDistance.setText(info.distToNextStop.mDistanceStr + " " +
info.distToNextStop.getUnitsStr(mFrame.getContext()));
// Display remaining time to next stop.
final long hours = TimeUnit.SECONDS.toHours(info.timeToNextStop);
final long minutes = TimeUnit.SECONDS.toMinutes(info.timeToNextStop) % 60;
String timeString = "";
if (hours > 0)
timeString += String.valueOf(hours) + " " + mFrame.getResources().getString(R.string.hour);
timeString += String.valueOf(minutes) + " " + mFrame.getResources().getString(R.string.minute);
mNextStopRemainingTime.setText(timeString);
// ETA to next stop.
final String format = DateFormat.is24HourFormat(mFrame.getContext()) ? "HH:mm" : "h:mm a";
final LocalTime localTime = LocalTime.now().plusSeconds(info.timeToNextStop);
mNextStopEta.setText(localTime.format(DateTimeFormatter.ofPattern(format)));
}
else
{
// Hide next stop info.
UiUtils.hide(mNextStopFrame);
}
}
public void show(boolean show)
{
if (show && !UiUtils.isVisible(mFrame))

View file

@ -39,6 +39,10 @@ public class RoutingInfo
public final double speedLimitMps;
private final boolean speedCamLimitExceeded;
private final boolean shouldPlayWarningSignal;
// Next stop info.
public final int nextStopPos;
public final Distance distToNextStop;
public final int timeToNextStop;
/**
* IMPORTANT : Order of enum values MUST BE the same as native CarDirection enum.
@ -144,7 +148,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 shouldPlayWarningSignal, int nextStopPos, Distance distToNextStop, int timeToNextStop)
{
this.distToTarget = distToTarget;
this.distToTurn = distToTurn;
@ -161,6 +165,9 @@ public class RoutingInfo
this.speedLimitMps = speedLimitMps;
this.speedCamLimitExceeded = speedLimitExceeded;
this.shouldPlayWarningSignal = shouldPlayWarningSignal;
this.nextStopPos = nextStopPos;
this.distToNextStop = distToNextStop;
this.timeToNextStop = timeToNextStop;
}
public boolean isSpeedCamLimitExceeded()

View file

@ -0,0 +1,20 @@
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="oval">
<size
android:width="30dp"
android:height="30dp" />
<solid android:color="@android:color/transparent" />
<stroke android:color="#FFFFFF" android:width="2dp"/>
</shape>
</item>
<item
android:left="2dp"
android:right="2dp"
android:top="2dp"
android:bottom="2dp">
<shape android:shape="oval">
<solid android:color="#006C35" />
</shape>
</item>
</layer-list>

View file

@ -122,4 +122,86 @@
<include layout="@layout/nav_lanes" />
</FrameLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/next_stop_frame"
android:layout_width="130dp"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginTop="8dp"
android:background="?navNextNextTurnFrame"
app:layout_constraintLeft_toLeftOf="@id/nav_next_turn_container"
app:layout_constraintTop_toBottomOf="@id/nav_next_turn_container">
<ImageView
android:id="@+id/ic_route_stop"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_margin="2dp"
android:gravity="center_vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_route_stop" />
<TextView
android:id="@+id/next_stop_pos"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_margin="8dp"
android:layout_marginStart="4dp"
android:background="@android:color/transparent"
android:gravity="center_vertical"
android:text="99"
android:textSize="18sp"
android:textColor="@color/white_primary"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="@id/ic_route_stop"
app:layout_constraintStart_toStartOf="@id/ic_route_stop"
app:layout_constraintEnd_toEndOf="@id/ic_route_stop"
app:layout_constraintTop_toTopOf="@id/ic_route_stop" />
<TextView
android:id="@+id/distance_to_next_stop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginTop="2dp"
android:background="@android:color/transparent"
android:text="9999 Km"
android:textSize="16sp"
android:textStyle="bold"
app:layout_constraintLeft_toRightOf="@id/ic_route_stop"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/remaining_time_to_next_stop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginTop="2dp"
android:layout_marginBottom="2dp"
android:background="@android:color/transparent"
android:text="99h 59 min"
android:textSize="16sp"
android:textStyle="bold"
app:layout_constraintLeft_toRightOf="@id/ic_route_stop"
app:layout_constraintTop_toBottomOf="@id/distance_to_next_stop" />
<TextView
android:id="@+id/eta_to_next_stop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginTop="2dp"
android:layout_marginBottom="2dp"
android:background="@android:color/transparent"
android:text="23:59"
android:textSize="16sp"
android:textStyle="bold"
app:layout_constraintLeft_toRightOf="@id/ic_route_stop"
app:layout_constraintTop_toBottomOf="@id/remaining_time_to_next_stop"
app:layout_constraintBottom_toBottomOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -122,4 +122,86 @@
<include layout="@layout/nav_lanes" />
</FrameLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/next_stop_frame"
android:layout_width="130dp"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginTop="8dp"
android:background="?navNextNextTurnFrame"
app:layout_constraintLeft_toLeftOf="@id/nav_next_turn_container"
app:layout_constraintTop_toBottomOf="@id/nav_next_turn_container">
<ImageView
android:id="@+id/ic_route_stop"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_margin="2dp"
android:gravity="center_vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_route_stop" />
<TextView
android:id="@+id/next_stop_pos"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_margin="8dp"
android:layout_marginStart="4dp"
android:background="@android:color/transparent"
android:gravity="center_vertical"
android:text="99"
android:textSize="18sp"
android:textColor="@color/white_primary"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="@id/ic_route_stop"
app:layout_constraintStart_toStartOf="@id/ic_route_stop"
app:layout_constraintEnd_toEndOf="@id/ic_route_stop"
app:layout_constraintTop_toTopOf="@id/ic_route_stop" />
<TextView
android:id="@+id/distance_to_next_stop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginTop="2dp"
android:background="@android:color/transparent"
android:text="9999 Km"
android:textSize="16sp"
android:textStyle="bold"
app:layout_constraintLeft_toRightOf="@id/ic_route_stop"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/remaining_time_to_next_stop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginTop="2dp"
android:layout_marginBottom="2dp"
android:background="@android:color/transparent"
android:text="99h 59 min"
android:textSize="16sp"
android:textStyle="bold"
app:layout_constraintLeft_toRightOf="@id/ic_route_stop"
app:layout_constraintTop_toBottomOf="@id/distance_to_next_stop" />
<TextView
android:id="@+id/eta_to_next_stop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginTop="2dp"
android:layout_marginBottom="2dp"
android:background="@android:color/transparent"
android:text="23:59"
android:textSize="16sp"
android:textStyle="bold"
app:layout_constraintLeft_toRightOf="@id/ic_route_stop"
app:layout_constraintTop_toBottomOf="@id/remaining_time_to_next_stop"
app:layout_constraintBottom_toBottomOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -150,6 +150,11 @@
<item name="android:textColor">?android:textColorSecondary</item>
</style>
<style name="MwmTextAppearance.NavMenu.NextStopNumber">
<item name="android:textSize">@dimen/text_size_nav_menu_dimension</item>
<item name="android:textColor">?android:textColorPrimary</item>
</style>
<style name="MwmTextAppearance.RoutingDetail">
<item name="android:textSize">@dimen/text_size_routing_plan_detail</item>
<item name="android:fontFamily">@string/robotoMedium</item>

View file

@ -24,6 +24,9 @@ public:
, m_time(0)
, m_completionPercent(0)
, m_pedestrianTurn(turns::PedestrianDirection::None)
, m_nextStopPos(0)
, m_distToNextStop(0.0)
, m_timeToNextStop(0)
{
}
@ -85,5 +88,10 @@ public:
// Current speed limit in meters per second.
// If no info about speed limit then m_speedLimitMps < 0.
double m_speedLimitMps = -1.0;
// Next stop information.
int m_nextStopPos;
platform::Distance m_distToNextStop;
int m_timeToNextStop;
};
} // namespace routing

View file

@ -20,6 +20,7 @@ namespace
{
double constexpr kOnEndToleranceM = 10.0;
double constexpr kSteetNameLinkMeters = 400.0;
double constexpr kMinimumETASec = 60.0;
} // namespace
std::string DebugPrint(RouteSegment::RoadNameInfo const & rni)
@ -79,6 +80,17 @@ double Route::GetCurrentDistanceToEndMeters() const
return m_poly.GetDistanceToEndMeters();
}
double Route::GetCurrentDistanceToSegmentMeters(size_t segIdx) const
{
if (!IsValid())
return 0.0;
double const endDistance = m_routeSegments[segIdx].GetDistFromBeginningMeters();
double const passedDistance = GetCurrentDistanceFromBeginMeters();
return endDistance - passedDistance;
}
double Route::GetMercatorDistanceFromBegin() const
{
auto const & curIter = m_poly.GetCurrentIter();

View file

@ -13,6 +13,7 @@
#include "traffic/speed_groups.hpp"
#include "platform/country_file.hpp"
#include "platform/distance.hpp"
#include "geometry/point_with_altitude.hpp"
#include "geometry/polyline2d.hpp"
@ -370,6 +371,7 @@ public:
double GetTotalDistanceMeters() const;
double GetCurrentDistanceFromBeginMeters() const;
double GetCurrentDistanceToEndMeters() const;
double GetCurrentDistanceToSegmentMeters(size_t segIdx) const;
double GetMercatorDistanceFromBegin() const;
/// \brief Extracts information about the nearest turn from the remaining part of the route.

View file

@ -444,6 +444,41 @@ void RoutingSession::GetRouteFollowingInfo(FollowingInfo & info) const
info.m_pedestrianTurn = (distanceToTurnMeters < kShowPedestrianTurnInMeters)
? turn.m_pedestrianTurn
: turns::PedestrianDirection::None;
// Next stop info.
size_t subrouteCount = m_route->GetSubrouteCount();
if (subrouteCount > 1)
{
size_t currentSubrouteIdx = m_route->GetCurrentSubrouteIdx();
if (currentSubrouteIdx >= (subrouteCount - 1))
{
// We're in the last subroute (no intermediate stops ahead).
info.m_nextStopPos = 0;
}
else
{
// Next stop pos.
info.m_nextStopPos = currentSubrouteIdx + 1;
// Get index of end segment of the current subroute.
size_t subrouteEndSegmentIdx = m_route->GetSubrouteAttrs(currentSubrouteIdx).GetEndSegmentIdx();
// Get remaining distance to end of current subroute.
info.m_distToNextStop = platform::Distance::CreateFormatted(
m_route->GetCurrentDistanceToSegmentMeters(subrouteEndSegmentIdx));
// Get remaining time to end of current subroute.
info.m_timeToNextStop = static_cast<int>(std::max(kMinimumETASec,
m_route->GetCurrentTimeToSegmentSec(subrouteEndSegmentIdx)));
}
}
else
{
// There's only one subroute (no intermediate stops).
info.m_nextStopPos = 0;
}
}
double RoutingSession::GetCompletionPercent() const