diff --git a/android/jni/app/organicmaps/Framework.cpp b/android/jni/app/organicmaps/Framework.cpp index 407e73e852..4fd22617f4 100644 --- a/android/jni/app/organicmaps/Framework.cpp +++ b/android/jni/app/organicmaps/Framework.cpp @@ -1425,6 +1425,7 @@ Java_app_organicmaps_Framework_nativeSetRouter(JNIEnv * env, jclass, jint router case 1: type = Type::Pedestrian; break; case 2: type = Type::Bicycle; break; case 3: type = Type::Transit; break; + case 4: type = Type::Ruler; break; default: assert(false); break; } g_framework->GetRoutingManager().SetRouter(type); diff --git a/android/res/drawable/ic_ruler_route.xml b/android/res/drawable/ic_ruler_route.xml new file mode 100644 index 0000000000..c502863276 --- /dev/null +++ b/android/res/drawable/ic_ruler_route.xml @@ -0,0 +1,31 @@ + + + + + + + diff --git a/android/res/layout/routing_bottom_panel_transit.xml b/android/res/layout/routing_bottom_panel_transit.xml index 22156f22ad..cce2c490be 100644 --- a/android/res/layout/routing_bottom_panel_transit.xml +++ b/android/res/layout/routing_bottom_panel_transit.xml @@ -46,7 +46,7 @@ android:id="@+id/transit_recycler_view" android:layout_marginTop="@dimen/margin_half_plus" android:layout_alignParentStart="true" - android:layout_below="@id/total_distance" + android:layout_below="@id/total_time" android:layout_width="match_parent" android:layout_height="wrap_content"/> diff --git a/android/res/layout/routing_plan.xml b/android/res/layout/routing_plan.xml index 1ca805f769..23382f782f 100644 --- a/android/res/layout/routing_plan.xml +++ b/android/res/layout/routing_plan.xml @@ -73,6 +73,14 @@ android:layout_marginEnd="12dp" tools:button="@drawable/ic_bike" tools:buttonTint="?iconTintLight" /> + + + + diff --git a/android/res/values/font_sizes.xml b/android/res/values/font_sizes.xml index 63aee99c7a..c118412a70 100644 --- a/android/res/values/font_sizes.xml +++ b/android/res/values/font_sizes.xml @@ -51,6 +51,7 @@ 20sp 16sp 14sp + 20sp 56sp 17sp diff --git a/android/res/values/themes-attrs.xml b/android/res/values/themes-attrs.xml index a5bd6ca2cb..d5b1c19b8c 100644 --- a/android/res/values/themes-attrs.xml +++ b/android/res/values/themes-attrs.xml @@ -42,6 +42,7 @@ + diff --git a/android/res/values/themes-base.xml b/android/res/values/themes-base.xml index da9956219d..79aeb84d0d 100644 --- a/android/res/values/themes-base.xml +++ b/android/res/values/themes-base.xml @@ -87,6 +87,7 @@ @drawable/back_arrow @color/black_4 + @color/black_4 @drawable/dot_divider @drawable/ic_layers_traffic_active @drawable/ic_layers_subway_active @@ -221,6 +222,7 @@ @drawable/back_arrow @color/white_4 + @color/white_4 @drawable/dot_divider_night @drawable/ic_layers_traffic_active_night @drawable/ic_layers_subway_active_night diff --git a/android/src/app/organicmaps/Framework.java b/android/src/app/organicmaps/Framework.java index 1b84662aab..7cbb8d1ab5 100644 --- a/android/src/app/organicmaps/Framework.java +++ b/android/src/app/organicmaps/Framework.java @@ -46,7 +46,7 @@ public class Framework public static final int MAP_STYLE_VEHICLE_DARK = 4; @Retention(RetentionPolicy.SOURCE) - @IntDef({ ROUTER_TYPE_VEHICLE, ROUTER_TYPE_PEDESTRIAN, ROUTER_TYPE_BICYCLE, ROUTER_TYPE_TRANSIT }) + @IntDef({ ROUTER_TYPE_VEHICLE, ROUTER_TYPE_PEDESTRIAN, ROUTER_TYPE_BICYCLE, ROUTER_TYPE_TRANSIT, ROUTER_TYPE_RULER }) public @interface RouterType {} @@ -54,6 +54,7 @@ public class Framework public static final int ROUTER_TYPE_PEDESTRIAN = 1; public static final int ROUTER_TYPE_BICYCLE = 2; public static final int ROUTER_TYPE_TRANSIT = 3; + public static final int ROUTER_TYPE_RULER = 4; @Retention(RetentionPolicy.SOURCE) @IntDef({DO_AFTER_UPDATE_NOTHING, DO_AFTER_UPDATE_AUTO_UPDATE, DO_AFTER_UPDATE_ASK_FOR_UPDATE}) diff --git a/android/src/app/organicmaps/routing/RoutingBottomMenuController.java b/android/src/app/organicmaps/routing/RoutingBottomMenuController.java index 35ab883c59..90c31c84a0 100644 --- a/android/src/app/organicmaps/routing/RoutingBottomMenuController.java +++ b/android/src/app/organicmaps/routing/RoutingBottomMenuController.java @@ -29,13 +29,17 @@ import androidx.recyclerview.widget.RecyclerView; import app.organicmaps.Framework; import app.organicmaps.R; +import app.organicmaps.bookmarks.data.DistanceAndAzimut; import app.organicmaps.location.LocationHelper; +import app.organicmaps.util.Distance; import app.organicmaps.widget.recycler.DotDividerItemDecoration; import app.organicmaps.widget.recycler.MultilineLayoutManager; import app.organicmaps.util.Graphics; import app.organicmaps.util.ThemeUtils; import app.organicmaps.util.UiUtils; +import java.util.LinkedList; +import java.util.List; import java.util.Locale; final class RoutingBottomMenuController implements View.OnClickListener @@ -135,9 +139,11 @@ final class RoutingBottomMenuController implements View.OnClickListener void showAltitudeChartAndRoutingDetails() { - UiUtils.hide(mError, mActionFrame, mTransitFrame); + UiUtils.hide(mError, mActionFrame, mAltitudeChart, mAltitudeDifference, mTransitFrame); - showRouteAltitudeChart(); + if (!RoutingController.get().isVehicleRouterType() && + !RoutingController.get().isRulerRouterType()) + showRouteAltitudeChart(); showRoutingDetails(); UiUtils.show(mAltitudeChartFrame); } @@ -172,6 +178,53 @@ final class RoutingBottomMenuController implements View.OnClickListener distanceView.setText(info.getTotalPedestrianDistance() + " " + info.getTotalPedestrianDistanceUnits()); } + @SuppressLint("SetTextI18n") + void showRulerInfo(@NonNull RouteMarkData[] points, Distance totalLength) + { + UiUtils.hide(mError, mAltitudeChartFrame, mActionFrame, mAltitudeChartFrame); + showStartButton(false); + UiUtils.show(mTransitFrame); + RecyclerView rv = mTransitFrame.findViewById(R.id.transit_recycler_view); + if (points.length > 2) + { + UiUtils.show(rv); + TransitStepAdapter adapter = new TransitStepAdapter(); + rv.setLayoutManager(new MultilineLayoutManager()); + rv.setNestedScrollingEnabled(false); + rv.removeItemDecoration(mTransitViewDecorator); + rv.addItemDecoration(mTransitViewDecorator); + rv.setAdapter(adapter); + adapter.setItems(pointsToRulerSteps(points)); + } + else + UiUtils.hide(rv); // Show only distance between start and finish + + TextView totalTimeView = mTransitFrame.findViewById(R.id.total_time); + totalTimeView.setText(mContext.getString(R.string.placepage_distance) + ": " + + totalLength.mDistanceStr + " " + totalLength.getUnitsStr(mContext)); + + UiUtils.hide(mTransitFrame, R.id.dot); + UiUtils.hide(mTransitFrame, R.id.pedestrian_icon); + UiUtils.hide(mTransitFrame, R.id.total_distance); + } + + // Create steps info to use in TransitStepAdapter. + private List pointsToRulerSteps(RouteMarkData[] points) + { + List transitSteps = new LinkedList<>(); + for (int i = 1; i < points.length; i++) + { + RouteMarkData segmentStart = points[i - 1]; + RouteMarkData segmentEnd = points[i]; + DistanceAndAzimut dist = Framework.nativeGetDistanceAndAzimuthFromLatLon(segmentStart.mLat, segmentStart.mLon, segmentEnd.mLat, segmentEnd.mLon, 0); + if (i > 1) + transitSteps.add(TransitStepInfo.intermediatePoint(i - 2)); + transitSteps.add(TransitStepInfo.ruler(dist.getDistance().mDistanceStr, dist.getDistance().getUnitsStr(mContext))); + } + + return transitSteps; + } + void showAddStartFrame() { UiUtils.hide(mError, mTransitFrame); @@ -206,15 +259,17 @@ final class RoutingBottomMenuController implements View.OnClickListener UiUtils.hide(mActionFrame); } - void setStartButton() + void setStartButton(boolean show) { - mStart.setText(mContext.getText(R.string.p2p_start)); - mStart.setOnClickListener(v -> { - if (mListener != null) - mListener.onRoutingStart(); - }); + if (show) { + mStart.setText(mContext.getText(R.string.p2p_start)); + mStart.setOnClickListener(v -> { + if (mListener != null) + mListener.onRoutingStart(); + }); + } - showStartButton(true); + showStartButton(show); } private void showError(@NonNull String message) diff --git a/android/src/app/organicmaps/routing/RoutingController.java b/android/src/app/organicmaps/routing/RoutingController.java index 2461fb5664..5ac320a141 100644 --- a/android/src/app/organicmaps/routing/RoutingController.java +++ b/android/src/app/organicmaps/routing/RoutingController.java @@ -603,6 +603,11 @@ public class RoutingController implements Initializable return mLastRouterType == Framework.ROUTER_TYPE_VEHICLE; } + boolean isRulerRouterType() + { + return mLastRouterType == Framework.ROUTER_TYPE_RULER; + } + public boolean isNavigating() { return mState == State.NAVIGATION; @@ -815,22 +820,14 @@ public class RoutingController implements Initializable } if (isSamePoint) - { - Logger.d(TAG, "setEndPoint: skip the same end point"); return false; - } if (point != null && point.sameAs(startPoint)) { if (endPoint == null) - { - Logger.d(TAG, "setEndPoint: skip because end point is empty"); return false; - } - Logger.d(TAG, "setEndPoint: swap with starting point"); startPoint = endPoint; - } endPoint = point; diff --git a/android/src/app/organicmaps/routing/RoutingPlanController.java b/android/src/app/organicmaps/routing/RoutingPlanController.java index f3ca006bf4..8c895613cd 100644 --- a/android/src/app/organicmaps/routing/RoutingPlanController.java +++ b/android/src/app/organicmaps/routing/RoutingPlanController.java @@ -39,6 +39,8 @@ public class RoutingPlanController extends ToolbarController private final WheelProgressView mProgressTransit; @NonNull private final WheelProgressView mProgressBicycle; + @NonNull + private final WheelProgressView mProgressRuler; // @NonNull // private final WheelProgressView mProgressTaxi; @@ -93,6 +95,7 @@ public class RoutingPlanController extends ToolbarController mProgressPedestrian = progressFrame.findViewById(R.id.progress_pedestrian); mProgressTransit = progressFrame.findViewById(R.id.progress_transit); mProgressBicycle = progressFrame.findViewById(R.id.progress_bicycle); + mProgressRuler = progressFrame.findViewById(R.id.progress_ruler); // mProgressTaxi = (WheelProgressView) progressFrame.findViewById(R.id.progress_taxi); mRoutingBottomMenuController = RoutingBottomMenuController.newInstance(requireActivity(), mFrame, listener); @@ -128,12 +131,13 @@ public class RoutingPlanController extends ToolbarController { setupRouterButton(R.id.vehicle, R.drawable.ic_car, this::onVehicleModeSelected); setupRouterButton(R.id.pedestrian, R.drawable.ic_pedestrian, this::onPedestrianModeSelected); - setupRouterButton(R.id.bicycle, R.drawable.ic_bike, this::onBicycleModeSelected); // setupRouterButton(R.id.taxi, R.drawable.ic_taxi, this::onTaxiModeSelected); - setupRouterButton(R.id.transit, R.drawable.ic_transit, v -> onTransitModeSelected()); + setupRouterButton(R.id.transit, R.drawable.ic_transit, this::onTransitModeSelected); + setupRouterButton(R.id.bicycle, R.drawable.ic_bike, this::onBicycleModeSelected); + setupRouterButton(R.id.ruler, R.drawable.ic_ruler_route, this::onRulerModeSelected); } - private void onTransitModeSelected() + private void onTransitModeSelected(@NonNull View v) { RoutingController.get().setRouterType(Framework.ROUTER_TYPE_TRANSIT); } @@ -143,6 +147,11 @@ public class RoutingPlanController extends ToolbarController RoutingController.get().setRouterType(Framework.ROUTER_TYPE_BICYCLE); } + private void onRulerModeSelected(@NonNull View v) + { + RoutingController.get().setRouterType(Framework.ROUTER_TYPE_RULER); + } + private void onPedestrianModeSelected(@NonNull View v) { RoutingController.get().setRouterType(Framework.ROUTER_TYPE_PEDESTRIAN); @@ -188,39 +197,53 @@ public class RoutingPlanController extends ToolbarController return; } - mRoutingBottomMenuController.setStartButton(); + if (isRulerType()) + { + RoutingInfo routingInfo = RoutingController.get().getCachedRoutingInfo(); + if (routingInfo != null) + mRoutingBottomMenuController.showRulerInfo(Framework.nativeGetRoutePoints(), routingInfo.distToTarget); + return; + } + + boolean showStartButton = !RoutingController.get().isRulerRouterType(); + mRoutingBottomMenuController.setStartButton(showStartButton); mRoutingBottomMenuController.showAltitudeChartAndRoutingDetails(); } public void updateBuildProgress(int progress, @Framework.RouterType int router) { UiUtils.invisible(mProgressVehicle, mProgressPedestrian, mProgressTransit, - mProgressBicycle); + mProgressBicycle, mProgressRuler); WheelProgressView progressView; - if (router == Framework.ROUTER_TYPE_VEHICLE) + switch(router) { + case Framework.ROUTER_TYPE_VEHICLE: mRouterTypes.check(R.id.vehicle); progressView = mProgressVehicle; - } - else if (router == Framework.ROUTER_TYPE_PEDESTRIAN) - { + break; + case Framework.ROUTER_TYPE_PEDESTRIAN: mRouterTypes.check(R.id.pedestrian); progressView = mProgressPedestrian; - } -// else if (router == Framework.ROUTER_TYPE_TAXI) -// { -// mRouterTypes.check(R.id.taxi); -// progressView = mProgressTaxi; -// } - else if (router == Framework.ROUTER_TYPE_TRANSIT) - { + break; + //case Framework.ROUTER_TYPE_TAXI: + // { + // mRouterTypes.check(R.id.taxi); + // progressView = mProgressTaxi; + // } + case Framework.ROUTER_TYPE_TRANSIT: mRouterTypes.check(R.id.transit); progressView = mProgressTransit; - } - else - { + break; + case Framework.ROUTER_TYPE_BICYCLE: mRouterTypes.check(R.id.bicycle); progressView = mProgressBicycle; + break; + case Framework.ROUTER_TYPE_RULER: + mRouterTypes.check(R.id.ruler); + progressView = mProgressRuler; + break; + default: + throw new IllegalArgumentException("unknown router: " + router); } RoutingToolbarButton button = mRouterTypes @@ -246,6 +269,11 @@ public class RoutingPlanController extends ToolbarController return RoutingController.get().isTransitType(); } + private boolean isRulerType() + { + return RoutingController.get().isRulerRouterType(); + } + void saveRoutingPanelState(@NonNull Bundle outState) { mRoutingBottomMenuController.saveRoutingPanelState(outState); diff --git a/android/src/app/organicmaps/routing/TransitStepInfo.java b/android/src/app/organicmaps/routing/TransitStepInfo.java index 64b3a517fb..9bcff82c4d 100644 --- a/android/src/app/organicmaps/routing/TransitStepInfo.java +++ b/android/src/app/organicmaps/routing/TransitStepInfo.java @@ -18,10 +18,11 @@ public class TransitStepInfo private static final int TRANSIT_TYPE_TRAIN = 3; private static final int TRANSIT_TYPE_LIGHT_RAIL = 4; private static final int TRANSIT_TYPE_MONORAIL = 5; + private static final int TRANSIT_TYPE_RULER = 6; @Retention(RetentionPolicy.SOURCE) @IntDef({ TRANSIT_TYPE_INTERMEDIATE_POINT, TRANSIT_TYPE_PEDESTRIAN, TRANSIT_TYPE_SUBWAY, - TRANSIT_TYPE_TRAIN, TRANSIT_TYPE_LIGHT_RAIL, TRANSIT_TYPE_MONORAIL }) + TRANSIT_TYPE_TRAIN, TRANSIT_TYPE_LIGHT_RAIL, TRANSIT_TYPE_MONORAIL, TRANSIT_TYPE_RULER}) @interface TransitType {} @NonNull @@ -48,6 +49,18 @@ public class TransitStepInfo mIntermediateIndex = intermediateIndex; } + @NonNull + public static TransitStepInfo intermediatePoint(int intermediateIndex) + { + return new TransitStepInfo(TRANSIT_TYPE_INTERMEDIATE_POINT, null, null, 0, null, 0, intermediateIndex); + } + + @NonNull + public static TransitStepInfo ruler(@NonNull String distance, @NonNull String distanceUnits) + { + return new TransitStepInfo(TRANSIT_TYPE_RULER, distance, distanceUnits, 0, null, 0, -1); + } + @NonNull public TransitStepType getType() { diff --git a/android/src/app/organicmaps/routing/TransitStepType.java b/android/src/app/organicmaps/routing/TransitStepType.java index 4aea7389b8..bf1ffd9a6d 100644 --- a/android/src/app/organicmaps/routing/TransitStepType.java +++ b/android/src/app/organicmaps/routing/TransitStepType.java @@ -12,7 +12,8 @@ public enum TransitStepType SUBWAY(R.drawable.ic_20px_route_planning_metro), TRAIN(R.drawable.ic_20px_route_planning_train), LIGHT_RAIL(R.drawable.ic_20px_route_planning_lightrail), - MONORAIL(R.drawable.ic_20px_route_planning_monorail); + MONORAIL(R.drawable.ic_20px_route_planning_monorail), + RULER(R.drawable.ic_ruler_route); @DrawableRes private final int mDrawable; diff --git a/android/src/app/organicmaps/routing/TransitStepView.java b/android/src/app/organicmaps/routing/TransitStepView.java index 5955557530..87d5116828 100644 --- a/android/src/app/organicmaps/routing/TransitStepView.java +++ b/android/src/app/organicmaps/routing/TransitStepView.java @@ -85,6 +85,12 @@ public class TransitStepView extends View implements MultilineLayoutManager.Sque mDrawable = null; mText = String.valueOf(info.getIntermediateIndex() + 1); } + else if (mStepType == TransitStepType.RULER) + { + mDrawable = null; + mText = info.getDistance() + " " + info.getDistanceUnits(); + mTextPaint.setColor(Color.BLACK); + } else { mDrawable = ResourcesCompat.getDrawable(getResources(), mStepType.getDrawable(), null); @@ -101,6 +107,8 @@ public class TransitStepView extends View implements MultilineLayoutManager.Sque { case PEDESTRIAN: return ThemeUtils.getColor(context, R.attr.transitPedestrianBackground); + case RULER: + return ThemeUtils.getColor(context, R.attr.transitRulerBackground); case INTERMEDIATE_POINT: return ThemeUtils.getColor(context, R.attr.colorPrimary); default: