diff --git a/OsmAnd-java/src/main/java/net/osmand/binary/RouteDataObject.java b/OsmAnd-java/src/main/java/net/osmand/binary/RouteDataObject.java index d70d83cf3a..28384d0bcb 100644 --- a/OsmAnd-java/src/main/java/net/osmand/binary/RouteDataObject.java +++ b/OsmAnd-java/src/main/java/net/osmand/binary/RouteDataObject.java @@ -676,7 +676,72 @@ public class RouteDataObject { } return false; } - + + public boolean isExitPoint() { + if (pointTypes != null) { + int ptSz = pointTypes.length; + for (int i = 0; i < ptSz; i++) { + int[] point = pointTypes[i]; + if (point != null) { + int pSz = point.length; + for (int j = 0; j < pSz; j++) { + if (region.routeEncodingRules.get(point[j]).getValue().equals("motorway_junction")) { + return true; + } + } + } + } + } + return false; + } + +// public boolean isMotorWayLink() { +// int sz = types.length; +// for (int i = 0; i < sz; i++) { +// RouteTypeRule r = region.quickGetEncodingRule(types[i]); +// if (r.getTag().equals("highway") && r.getValue().equals("motorway_link")) { +// return true; +// } +// } +// return false; +// } + + public String getExitName() { + if (pointNames != null && pointNameTypes != null) { + int pnSz = pointNames.length; + for (int i = 0; i < pnSz; i++) { + String[] point = pointNames[i]; + if (point != null) { + int pSz = point.length; + for (int j = 0; j < pSz; j++) { + if (pointNameTypes[i][j] == region.nameTypeRule) { + return point[j]; + } + } + } + } + } + return null; + } + + public String getExitRef() { + if (pointNames != null && pointNameTypes != null) { + int pnSz = pointNames.length; + for (int i = 0; i < pnSz; i++) { + String[] point = pointNames[i]; + if (point != null) { + int pSz = point.length; + for (int j = 0; j < pSz; j++) { + if (pointNameTypes[i][j] == region.refTypeRule) { + return point[j]; + } + } + } + } + } + return null; + } + public int getOneway() { int sz = types.length; for (int i = 0; i < sz; i++) { diff --git a/OsmAnd-java/src/main/java/net/osmand/router/ExitInfo.java b/OsmAnd-java/src/main/java/net/osmand/router/ExitInfo.java new file mode 100644 index 0000000000..3f51d0f9d0 --- /dev/null +++ b/OsmAnd-java/src/main/java/net/osmand/router/ExitInfo.java @@ -0,0 +1,25 @@ +package net.osmand.router; + + +public class ExitInfo { + + private String ref; + + private String exitStreetName; + + public String getRef() { + return ref; + } + + public void setRef(String ref) { + this.ref = ref; + } + + public String getExitStreetName() { + return exitStreetName; + } + + public void setExitStreetName(String exitStreetName) { + this.exitStreetName = exitStreetName; + } +} diff --git a/OsmAnd/res/drawable/bg_topbar_shield_exit_ref.xml b/OsmAnd/res/drawable/bg_topbar_shield_exit_ref.xml new file mode 100644 index 0000000000..fff0e2dbf0 --- /dev/null +++ b/OsmAnd/res/drawable/bg_topbar_shield_exit_ref.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/OsmAnd/res/layout-land/map_hud_top.xml b/OsmAnd/res/layout-land/map_hud_top.xml index f017986c92..e74c33eec6 100644 --- a/OsmAnd/res/layout-land/map_hud_top.xml +++ b/OsmAnd/res/layout-land/map_hud_top.xml @@ -335,43 +335,74 @@ android:background="@drawable/btn_round" android:minHeight="@dimen/map_address_height"> - + + + android:background="@drawable/bg_topbar_shield_exit_ref" + android:gravity="center" + android:minWidth="@dimen/map_widget_height" + android:textColor="@color/color_white" + android:textSize="@dimen/map_widget_text_size" + android:visibility="gone" + tools:text="8" + tools:visibility="visible" /> - + + + + - + android:layout_height="wrap_content"> - - + + - + + + - - + + + - + tools:visibility="visible" /> - - - + + + + + + + + + + + + - + android:orientation="vertical"> + + + + + + diff --git a/OsmAnd/src/net/osmand/plus/render/OsmandRenderer.java b/OsmAnd/src/net/osmand/plus/render/OsmandRenderer.java index 9d600c4c1f..6592198107 100644 --- a/OsmAnd/src/net/osmand/plus/render/OsmandRenderer.java +++ b/OsmAnd/src/net/osmand/plus/render/OsmandRenderer.java @@ -49,6 +49,8 @@ import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Shader; import android.graphics.Shader.TileMode; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; import android.os.Handler; import android.os.Looper; import android.util.DisplayMetrics; @@ -389,6 +391,11 @@ public class OsmandRenderer { } } + public Drawable getShieldDrawable(String shieldId){ + Bitmap shield = RenderingIcons.getIcon(context, shieldId, true); + return new BitmapDrawable(context.getResources(),shield); + } + protected void drawBitmap(Canvas cv, Bitmap ico, RectF rf) { if(ico == null) { return; diff --git a/OsmAnd/src/net/osmand/plus/render/TextRenderer.java b/OsmAnd/src/net/osmand/plus/render/TextRenderer.java index 429db5200b..4df7d5db34 100644 --- a/OsmAnd/src/net/osmand/plus/render/TextRenderer.java +++ b/OsmAnd/src/net/osmand/plus/render/TextRenderer.java @@ -43,7 +43,7 @@ public class TextRenderer { private Typeface italicTypeface; private Typeface boldTypeface; - static class TextDrawInfo { + public static class TextDrawInfo { public TextDrawInfo(String text) { this.text = text; @@ -95,6 +95,30 @@ public class TextRenderer { } textOrder = render.getIntPropertyValue(render.ALL.R_TEXT_ORDER, 100); } + + public float getCenterX() { + return centerX; + } + + public void setCenterX(float centerX) { + this.centerX = centerX; + } + + public float getCenterY() { + return centerY; + } + + public void setCenterY(float centerY) { + this.centerY = centerY; + } + + public String getShieldResIcon() { + return shieldResIcon; + } + + public void setShieldResIcon(String shieldResIcon) { + this.shieldResIcon = shieldResIcon; + } } public TextRenderer(Context context) { @@ -288,13 +312,13 @@ public class TextRenderer { } } - private void drawShieldIcon(RenderingContext rc, Canvas cv, TextDrawInfo text, String sr) { + public void drawShieldIcon(RenderingContext rc, Canvas cv, TextDrawInfo text, String sr) { if (sr != null) { float coef = rc.getDensityValue(rc.screenDensityRatio * rc.textScale); Bitmap ico = RenderingIcons.getIcon(context, sr, true); if (ico != null) { - float left = text.centerX - ico.getWidth() / 2 * coef - 0.5f; - float top = text.centerY - ico.getHeight() / 2 * coef - paintText.descent() - 0.5f; + float left = text.centerX - ico.getWidth() / 2 * coef;// - 0.5f; + float top = text.centerY - ico.getHeight() / 2 * coef - paintText.descent() * 1.5f; if(rc.screenDensityRatio != 1f){ RectF rf = new RectF(left, top, left + ico.getWidth() * coef, top + ico.getHeight() * coef); @@ -308,7 +332,7 @@ public class TextRenderer { } } - private void drawWrappedText(Canvas cv, TextDrawInfo text, float textSize) { + public void drawWrappedText(Canvas cv, TextDrawInfo text, float textSize) { if (text.textWrap == 0) { // set maximum for all text text.textWrap = 40; diff --git a/OsmAnd/src/net/osmand/plus/routepreparationmenu/cards/RouteDirectionsCard.java b/OsmAnd/src/net/osmand/plus/routepreparationmenu/cards/RouteDirectionsCard.java index 09380e43d6..d56950cf6f 100644 --- a/OsmAnd/src/net/osmand/plus/routepreparationmenu/cards/RouteDirectionsCard.java +++ b/OsmAnd/src/net/osmand/plus/routepreparationmenu/cards/RouteDirectionsCard.java @@ -17,6 +17,7 @@ import net.osmand.plus.activities.MapActivity; import net.osmand.plus.routepreparationmenu.RouteDetailsFragment; import net.osmand.plus.routing.RouteDirectionInfo; import net.osmand.plus.views.TurnPathHelper; +import net.osmand.plus.views.mapwidgets.RouteInfoWidgetsFactory; import net.osmand.util.Algorithms; import java.util.List; @@ -67,13 +68,25 @@ public class RouteDirectionsCard extends BaseCard { TextView timeLabel = (TextView) row.findViewById(R.id.time); TextView cumulativeDistanceLabel = (TextView) row.findViewById(R.id.cumulative_distance); TextView cumulativeTimeLabel = (TextView) row.findViewById(R.id.cumulative_time); - ImageView icon = (ImageView) row.findViewById(R.id.direction); + ImageView directionIcon = (ImageView) row.findViewById(R.id.direction); + ImageView lanesIcon = (ImageView) row.findViewById(R.id.lanes); row.findViewById(R.id.divider).setVisibility(directionInfoIndex == directionsInfo.size() - 1 ? View.INVISIBLE : View.VISIBLE); TurnPathHelper.RouteDrawable drawable = new TurnPathHelper.RouteDrawable(mapActivity.getResources(), true); drawable.setColorFilter(new PorterDuffColorFilter(getActiveColor(), PorterDuff.Mode.SRC_ATOP)); drawable.setRouteType(model.getTurnType()); - icon.setImageDrawable(drawable); + directionIcon.setImageDrawable(drawable); + + int[] lanes = model.getTurnType().getLanes(); + if (lanes != null){ + RouteInfoWidgetsFactory.LanesDrawable lanesDrawable = new RouteInfoWidgetsFactory.LanesDrawable(mapActivity,1); + lanesDrawable.lanes = lanes; + lanesDrawable.isTurnByTurn = true; + lanesDrawable.isNightMode = nightMode; + lanesDrawable.updateBounds(); + lanesIcon.setImageDrawable(lanesDrawable); + lanesIcon.setVisibility(View.VISIBLE); + } label.setText(model.getDescriptionRoutePart()); if (model.distance > 0) { diff --git a/OsmAnd/src/net/osmand/plus/routing/RouteCalculationResult.java b/OsmAnd/src/net/osmand/plus/routing/RouteCalculationResult.java index 198b3c8cff..2faaca1a3a 100644 --- a/OsmAnd/src/net/osmand/plus/routing/RouteCalculationResult.java +++ b/OsmAnd/src/net/osmand/plus/routing/RouteCalculationResult.java @@ -4,6 +4,7 @@ import android.content.Context; import android.support.annotation.Nullable; import net.osmand.Location; +import net.osmand.PlatformUtil; import net.osmand.binary.BinaryMapRouteReaderAdapter.RouteRegion; import net.osmand.binary.BinaryMapRouteReaderAdapter.RouteTypeRule; import net.osmand.binary.RouteDataObject; @@ -14,12 +15,15 @@ import net.osmand.plus.ApplicationMode; import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; import net.osmand.plus.routing.AlarmInfo.AlarmInfoType; +import net.osmand.router.ExitInfo; import net.osmand.router.RouteSegmentResult; import net.osmand.router.RoutingContext; import net.osmand.router.TurnType; import net.osmand.util.Algorithms; import net.osmand.util.MapUtils; +import org.apache.commons.logging.Log; + import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -27,6 +31,8 @@ import java.util.List; import static net.osmand.binary.RouteDataObject.HEIGHT_UNDEFINED; public class RouteCalculationResult { + private final static Log log = PlatformUtil.getLog(RouteCalculationResult.class); + private static double distanceClosestToIntermediate = 3000; private static double distanceThresholdToIntermediate = 25; // could not be null and immodifiable! @@ -318,15 +324,55 @@ public class RouteCalculationResult { info.routeEndPointOffset = roundAboutEnd; } RouteSegmentResult next = list.get(lind); - info.setRef(next.getObject().getRef(ctx.getSettings().MAP_PREFERRED_LOCALE.get(), - ctx.getSettings().MAP_TRANSLITERATE_NAMES.get(), next.isForwardDirection())); + String ref = next.getObject().getRef(ctx.getSettings().MAP_PREFERRED_LOCALE.get(), + ctx.getSettings().MAP_TRANSLITERATE_NAMES.get(), next.isForwardDirection()); + info.setRef(ref); info.setStreetName(next.getObject().getName(ctx.getSettings().MAP_PREFERRED_LOCALE.get(), ctx.getSettings().MAP_TRANSLITERATE_NAMES.get())); info.setDestinationName(next.getObject().getDestinationName(ctx.getSettings().MAP_PREFERRED_LOCALE.get(), ctx.getSettings().MAP_TRANSLITERATE_NAMES.get(), next.isForwardDirection())); + if (s.getObject().isExitPoint() && next.getObject().getHighway().equals("motorway_link")) { + ExitInfo exitInfo = new ExitInfo(); + exitInfo.setRef(next.getObject().getExitRef()); + exitInfo.setExitStreetName(next.getObject().getExitName()); + info.setExitInfo(exitInfo); + } + + if (ref != null) { + RouteDataObject nextRoad = next.getObject(); + boolean isNextShieldFound = false; + int[] nextSegmentNameIds = nextRoad.nameIds; + for (int nm = 0; nm < nextSegmentNameIds.length; nm++) { + if (nextRoad.region.quickGetEncodingRule(nextSegmentNameIds[nm]).getTag().startsWith("road_ref")) { + info.setRouteDataObject(nextRoad); + isNextShieldFound = true; + } + } + + if (!isNextShieldFound) { + for (int ind = lind; ind < list.size(); ind++) { + if (list.get(ind).getTurnType() != null) { + info.setRouteDataObject(null); + break; + } else { + RouteDataObject obj = list.get(ind).getObject(); + int[] nameIds = obj.nameIds; + for (int idx = 0; idx < nameIds.length; idx ++) { + if (obj.region.routeEncodingRules.get(obj.nameIds[idx]).getTag().startsWith("road_ref")) { + info.setRouteDataObject(obj); + break; + } + } + if (info.getRouteDataObject() != null) { + break; + } + } + } + } + } } - String description = toString(turn, ctx, false) + " " + RoutingHelper.formatStreetName(info.getStreetName(), + String description = toString(turn, ctx, false) + " " + RoutingHelper.formatStreetName(info.getStreetName(), info.getRef(), info.getDestinationName(), ctx.getString(R.string.towards)); description = description.trim(); String[] pointNames = s.getObject().getPointNames(s.getStartPointIndex()); diff --git a/OsmAnd/src/net/osmand/plus/routing/RouteDirectionInfo.java b/OsmAnd/src/net/osmand/plus/routing/RouteDirectionInfo.java index f83be5ce3b..9c503ccd82 100644 --- a/OsmAnd/src/net/osmand/plus/routing/RouteDirectionInfo.java +++ b/OsmAnd/src/net/osmand/plus/routing/RouteDirectionInfo.java @@ -1,7 +1,11 @@ package net.osmand.plus.routing; +import android.support.annotation.Nullable; + +import net.osmand.binary.RouteDataObject; import net.osmand.plus.OsmAndFormatter; import net.osmand.plus.OsmandApplication; +import net.osmand.router.ExitInfo; import net.osmand.router.TurnType; public class RouteDirectionInfo { @@ -15,13 +19,18 @@ public class RouteDirectionInfo { private String descriptionRoute = ""; //$NON-NLS-1$ // Speed after the action till next turn private float averageSpeed; - + private String ref; - + private String streetName; - + private String destinationName; + private RouteDataObject routeDataObject = null; + + @Nullable + private ExitInfo exitInfo; + public String getDestinationName() { return destinationName; } @@ -35,14 +44,22 @@ public class RouteDirectionInfo { this.averageSpeed = averageSpeed == 0 ? 1 : averageSpeed; this.turnType = turnType; } - + + public RouteDataObject getRouteDataObject() { + return routeDataObject; + } + + public void setRouteDataObject(RouteDataObject routeDataObject) { + this.routeDataObject = routeDataObject; + } + public String getDescriptionRoute(OsmandApplication ctx) { if (!descriptionRoute.endsWith(OsmAndFormatter.getFormattedDistance(distance, ctx))) { descriptionRoute += " " + OsmAndFormatter.getFormattedDistance(distance, ctx); } return descriptionRoute.trim(); } - + public String getDescriptionRoute(OsmandApplication ctx, int collectedDistance) { if (!descriptionRoute.endsWith(OsmAndFormatter.getFormattedDistance(collectedDistance, ctx))) { descriptionRoute += " " + OsmAndFormatter.getFormattedDistance(collectedDistance, ctx); @@ -69,7 +86,7 @@ public class RouteDirectionInfo { public void setStreetName(String streetName) { this.streetName = streetName; } - + public void setDescriptionRoute(String descriptionRoute) { this.descriptionRoute = descriptionRoute; } @@ -87,11 +104,11 @@ public class RouteDirectionInfo { return (int) Math.round(distance / averageSpeed); } - + public TurnType getTurnType() { return turnType; } - + // calculated vars // after action (excluding expectedTime) @@ -104,6 +121,15 @@ public class RouteDirectionInfo { } public void setDistance(int distance) { - this.distance = distance; + this.distance = distance; } -} + + @Nullable + public ExitInfo getExitInfo() { + return exitInfo; + } + + public void setExitInfo(@Nullable ExitInfo exitInfo) { + this.exitInfo = exitInfo; + } +} \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/routing/RoutingHelper.java b/OsmAnd/src/net/osmand/plus/routing/RoutingHelper.java index ce4a7861ec..0cfff0e39f 100644 --- a/OsmAnd/src/net/osmand/plus/routing/RoutingHelper.java +++ b/OsmAnd/src/net/osmand/plus/routing/RoutingHelper.java @@ -835,12 +835,12 @@ public class RoutingHelper { if(n.distanceTo > 0 && n.directionInfo != null && !n.directionInfo.getTurnType().isSkipToSpeak() && voiceRouter.isDistanceLess(speed, n.distanceTo, voiceRouter.PREPARE_DISTANCE * 0.75f, 0f)) { String nm = n.directionInfo.getStreetName(); - String rf = n.directionInfo.getRef(); +// String rf = n.directionInfo.getRef(); String dn = n.directionInfo.getDestinationName(); if(next != null) { next[0] = n.directionInfo.getTurnType(); } - return formatStreetName(nm, rf, dn, "»"); + return formatStreetName(nm, null, dn, "»"); } RouteSegmentResult rs = getCurrentSegmentResult(); if(rs != null) { @@ -864,10 +864,10 @@ public class RoutingHelper { private String getRouteSegmentStreetName(RouteSegmentResult rs) { String nm = rs.getObject().getName(settings.MAP_PREFERRED_LOCALE.get(), settings.MAP_TRANSLITERATE_NAMES.get()); - String rf = rs.getObject().getRef(settings.MAP_PREFERRED_LOCALE.get(), settings.MAP_TRANSLITERATE_NAMES.get(), rs.isForwardDirection()); +// String rf = rs.getObject().getRef(settings.MAP_PREFERRED_LOCALE.get(), settings.MAP_TRANSLITERATE_NAMES.get(), rs.isForwardDirection()); String dn = rs.getObject().getDestinationName(settings.MAP_PREFERRED_LOCALE.get(), settings.MAP_TRANSLITERATE_NAMES.get(), rs.isForwardDirection()); - return formatStreetName(nm, rf, dn, "»"); + return formatStreetName(nm, null, dn, "»"); } public RouteSegmentResult getCurrentSegmentResult() { diff --git a/OsmAnd/src/net/osmand/plus/routing/VoiceRouter.java b/OsmAnd/src/net/osmand/plus/routing/VoiceRouter.java index a05ebe5a40..80a2945f88 100644 --- a/OsmAnd/src/net/osmand/plus/routing/VoiceRouter.java +++ b/OsmAnd/src/net/osmand/plus/routing/VoiceRouter.java @@ -16,6 +16,7 @@ import net.osmand.plus.routing.data.StreetName; import net.osmand.plus.voice.AbstractPrologCommandPlayer; import net.osmand.plus.voice.CommandBuilder; import net.osmand.plus.voice.CommandPlayer; +import net.osmand.router.ExitInfo; import net.osmand.router.RouteSegmentResult; import net.osmand.router.TurnType; import net.osmand.util.MapUtils; @@ -618,13 +619,29 @@ public class VoiceRouter { } } else { - result.put("toRef", getNonNullString(getSpeakablePointName(i.getRef()))); + result.put(TO_REF, getNonNullString(getSpeakablePointName(i.getRef()))); result.put(TO_STREET_NAME, getNonNullString(getSpeakablePointName(i.getStreetName()))); result.put(TO_DEST, ""); } return new StreetName(result); } + private StreetName getSpeakableExitName(RouteDirectionInfo routeInfo, ExitInfo exitInfo, boolean includeDest) { + Map result = new HashMap<>(); + if (exitInfo == null || !router.getSettings().SPEAK_STREET_NAMES.get()) { + return new StreetName(result); + } + if (player != null && player.supportsStructuredStreetNames()) { + result.put(TO_REF, getNonNullString(getSpeakablePointName(exitInfo.getRef()))); + result.put(TO_STREET_NAME, getNonNullString(getSpeakablePointName(exitInfo.getExitStreetName()))); + result.put(TO_DEST, includeDest ? getNonNullString(getSpeakablePointName(routeInfo.getRef())) : ""); + } else { + result.put(TO_REF, getNonNullString(getSpeakablePointName(exitInfo.getRef()))); + result.put(TO_STREET_NAME, getNonNullString(getSpeakablePointName(exitInfo.getExitStreetName()))); + result.put(TO_DEST, ""); + } + return new StreetName(result); + } private String getNonNullString(String speakablePointName) { return speakablePointName == null ? "" : speakablePointName; @@ -677,8 +694,14 @@ public class VoiceRouter { if (p != null) { String tParam = getTurnType(next.getTurnType()); boolean isPlay = true; + ExitInfo exitInfo = next.getExitInfo(); + String lang = player.getLanguage(); if (tParam != null) { - p.turn(tParam, dist, getSpeakableStreetName(currentSegment, next, true)); + if (exitInfo != null) { + p.takeExit(tParam, dist, getSpeakableExitName(next, exitInfo, true)); + } else { + p.turn(tParam, dist, getSpeakableStreetName(currentSegment, next, true)); + } suppressDest = true; } else if (next.getTurnType().isRoundAbout()) { p.roundAbout(dist, next.getTurnType().getTurnAngle(), next.getTurnType().getExitOut(), getSpeakableStreetName(currentSegment, next, true)); @@ -744,9 +767,15 @@ public class VoiceRouter { CommandBuilder p = getNewCommandPlayerToPlay(); if (p != null) { String tParam = getTurnType(next.getTurnType()); + ExitInfo exitInfo = next.getExitInfo(); boolean isplay = true; + String lang = player.getLanguage(); if (tParam != null) { - p.turn(tParam, getSpeakableStreetName(currentSegment, next, !suppressDest)); + if (exitInfo != null) { + p.takeExit(tParam, getSpeakableExitName(next, exitInfo, !suppressDest)); + } else { + p.turn(tParam, getSpeakableStreetName(currentSegment, next, !suppressDest)); + } } else if (next.getTurnType().isRoundAbout()) { p.roundAbout(next.getTurnType().getTurnAngle(), next.getTurnType().getExitOut(), getSpeakableStreetName(currentSegment, next, !suppressDest)); } else if (next.getTurnType().getValue() == TurnType.TU || next.getTurnType().getValue() == TurnType.TRU) { diff --git a/OsmAnd/src/net/osmand/plus/views/mapwidgets/MapInfoWidgetsFactory.java b/OsmAnd/src/net/osmand/plus/views/mapwidgets/MapInfoWidgetsFactory.java index a58672ae60..2752dc3dc3 100644 --- a/OsmAnd/src/net/osmand/plus/views/mapwidgets/MapInfoWidgetsFactory.java +++ b/OsmAnd/src/net/osmand/plus/views/mapwidgets/MapInfoWidgetsFactory.java @@ -1,13 +1,15 @@ package net.osmand.plus.views.mapwidgets; -import android.animation.LayoutTransition; import android.annotation.SuppressLint; import android.app.Activity; import android.content.Context; import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.Paint; import android.graphics.Typeface; import android.graphics.drawable.Drawable; -import android.os.Build; import android.os.Bundle; import android.support.annotation.ColorInt; import android.support.annotation.ColorRes; @@ -19,6 +21,7 @@ import android.support.v4.content.ContextCompat; import android.support.v4.graphics.drawable.DrawableCompat; import android.support.v7.widget.SwitchCompat; import android.text.ClipboardManager; +import android.util.TypedValue; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; @@ -34,6 +37,8 @@ import com.jwetherell.openmap.common.UTMPoint; import net.osmand.AndroidUtils; import net.osmand.Location; import net.osmand.LocationConvert; +import net.osmand.PlatformUtil; +import net.osmand.binary.BinaryMapRouteReaderAdapter; import net.osmand.binary.RouteDataObject; import net.osmand.data.LatLon; import net.osmand.data.PointDescription; @@ -52,8 +57,11 @@ import net.osmand.plus.helpers.AndroidUiHelper; import net.osmand.plus.helpers.WaypointDialogHelper; import net.osmand.plus.helpers.WaypointHelper; import net.osmand.plus.helpers.WaypointHelper.LocationPointWrapper; +import net.osmand.plus.render.OsmandRenderer; +import net.osmand.plus.render.TextRenderer; import net.osmand.plus.routepreparationmenu.MapRouteInfoMenu; import net.osmand.plus.routepreparationmenu.ShowAlongTheRouteBottomSheet; +import net.osmand.plus.routing.RouteCalculationResult; import net.osmand.plus.routing.RouteDirectionInfo; import net.osmand.plus.routing.RoutingHelper; import net.osmand.plus.views.OsmandMapLayer.DrawSettings; @@ -61,10 +69,15 @@ import net.osmand.plus.views.OsmandMapTileView; import net.osmand.plus.views.RulerControlLayer; import net.osmand.plus.views.mapwidgets.MapWidgetRegistry.WidgetState; import net.osmand.plus.views.mapwidgets.NextTurnInfoWidget.TurnDrawable; +import net.osmand.render.RenderingRuleSearchRequest; +import net.osmand.render.RenderingRulesStorage; +import net.osmand.router.ExitInfo; import net.osmand.router.TurnType; import net.osmand.util.Algorithms; import net.osmand.util.MapUtils; +import org.apache.commons.logging.Log; + import java.util.Iterator; import java.util.LinkedList; @@ -903,6 +916,9 @@ public class MapInfoWidgetsFactory { private View topBar; private TextView addressText; private TextView addressTextShadow; + private TextView exitRefText; + private ImageView shieldIcon; + private ImageView turnIcon; private OsmAndLocationProvider locationProvider; private WaypointHelper waypointHelper; private OsmandSettings settings; @@ -911,12 +927,16 @@ public class MapInfoWidgetsFactory { private TurnDrawable turnDrawable; private boolean showMarker; private int shadowRad; + private static final Log LOG = PlatformUtil.getLog(TopTextView.class); public TopTextView(OsmandApplication app, MapActivity map) { topBar = map.findViewById(R.id.map_top_bar); addressText = (TextView) map.findViewById(R.id.map_address_text); addressTextShadow = (TextView) map.findViewById(R.id.map_address_text_shadow); waypointInfoBar = map.findViewById(R.id.waypoint_info_bar); + exitRefText = map.findViewById(R.id.map_exit_ref); + shieldIcon = map.findViewById(R.id.map_shield_icon); + turnIcon = map.findViewById(R.id.map_turn_icon); this.routingHelper = app.getRoutingHelper(); locationProvider = app.getLocationProvider(); this.map = map; @@ -940,6 +960,8 @@ public class MapInfoWidgetsFactory { TextInfoWidget.updateTextColor((TextView) waypointInfoBar.findViewById(R.id.waypoint_text), (TextView) waypointInfoBar.findViewById(R.id.waypoint_text_shadow), textColor, textShadowColor, bold, rad / 2); + exitRefText.setTextColor(nightMode ? map.getResources().getColor(R.color.text_color_primary_dark) : + map.getResources().getColor(R.color.color_white)); ImageView all = (ImageView) waypointInfoBar.findViewById(R.id.waypoint_more); ImageView remove = (ImageView) waypointInfoBar.findViewById(R.id.waypoint_close); @@ -955,6 +977,11 @@ public class MapInfoWidgetsFactory { TurnType[] type = new TurnType[1]; boolean showNextTurn = false; boolean showMarker = this.showMarker; + boolean showExitInfo = false; + boolean showShield = false; + ExitInfo exitInfo = null; + RouteDataObject object = null; + if (routingHelper != null && routingHelper.isRouteCalculated() && !routingHelper.isDeviatedFromRoute()) { if (routingHelper.isFollowingMode()) { if (settings.SHOW_STREET_NAME.get()) { @@ -968,6 +995,25 @@ public class MapInfoWidgetsFactory { turnDrawable.setColor(R.color.nav_arrow); } } + RouteCalculationResult.NextDirectionInfo nextDirInfo = routingHelper.getNextRouteDirectionInfo( + new RouteCalculationResult.NextDirectionInfo(), true); + RouteDirectionInfo directionInfo = nextDirInfo.directionInfo; + + if (directionInfo != null && directionInfo.getExitInfo() != null) { + exitInfo = directionInfo.getExitInfo(); + showExitInfo = true; + } else { + showExitInfo = false; + } + + if (showExitInfo) { + text = exitInfo.getExitStreetName(); + } + + if (nextDirInfo.directionInfo.getRouteDataObject() != null) { + object = nextDirInfo.directionInfo.getRouteDataObject(); + showShield = true; + } } } else { int di = MapRouteInfoMenu.getDirectionInfo(); @@ -977,10 +1023,7 @@ public class MapInfoWidgetsFactory { RouteDirectionInfo next = routingHelper.getRouteDirections().get(di); type[0] = next.getTurnType(); turnDrawable.setColor(R.color.nav_arrow_distant); - text = RoutingHelper.formatStreetName(next.getStreetName(), next.getRef(), next.getDestinationName(), "»"); -// if (next.distance > 0) { -// text += " " + OsmAndFormatter.getFormattedDistance(next.distance, map.getMyApplication()); -// } + text = RoutingHelper.formatStreetName(next.getStreetName(), null, next.getDestinationName(), "»"); if (text == null) { text = ""; } @@ -1029,25 +1072,37 @@ public class MapInfoWidgetsFactory { AndroidUiHelper.updateVisibility(addressTextShadow, shadowRad > 0); boolean update = turnDrawable.setTurnType(type[0]) || showMarker != this.showMarker; this.showMarker = showMarker; - int h = addressText.getHeight() / 4 * 3; - if (h != turnDrawable.getBounds().bottom) { - turnDrawable.setBounds(0, 0, h, h); + if (showShield) { + if (setRoadShield(shieldIcon, object)) { + AndroidUiHelper.updateVisibility(shieldIcon, true); + } else { + AndroidUiHelper.updateVisibility(shieldIcon, false); + } + } else { + AndroidUiHelper.updateVisibility(shieldIcon, false); + } + + if (showExitInfo) { + String exitRef = exitInfo.getRef(); + if (!Algorithms.isEmpty(exitRef)) { + exitRefText.setText(exitRef); + AndroidUiHelper.updateVisibility(exitRefText, true); + } else { + AndroidUiHelper.updateVisibility(exitRefText, false); + } + } else { + AndroidUiHelper.updateVisibility(exitRefText, false); } if (update) { if (type[0] != null) { - addressTextShadow.setCompoundDrawables(turnDrawable, null, null, null); - addressTextShadow.setCompoundDrawablePadding(4); - addressText.setCompoundDrawables(turnDrawable, null, null, null); - addressText.setCompoundDrawablePadding(4); + turnIcon.setImageDrawable(turnDrawable); + AndroidUiHelper.updateVisibility(turnIcon, true); } else if (showMarker) { Drawable marker = map.getMyApplication().getUIUtilities().getIcon(R.drawable.ic_action_start_navigation, R.color.color_myloc_distance); - addressTextShadow.setCompoundDrawablesWithIntrinsicBounds(marker, null, null, null); - addressTextShadow.setCompoundDrawablePadding(4); - addressText.setCompoundDrawablesWithIntrinsicBounds(marker, null, null, null); - addressText.setCompoundDrawablePadding(4); + turnIcon.setImageDrawable(marker); + AndroidUiHelper.updateVisibility(turnIcon, true); } else { - addressTextShadow.setCompoundDrawables(null, null, null, null); - addressText.setCompoundDrawables(null, null, null, null); + AndroidUiHelper.updateVisibility(turnIcon, false); } } if (!text.equals(addressText.getText().toString())) { @@ -1059,6 +1114,102 @@ public class MapInfoWidgetsFactory { return false; } + private boolean setRoadShield(ImageView view, RouteDataObject object) { + + String nameTag = null; + String name = null; + StringBuilder additional = new StringBuilder(); + for (int i = 0; i < object.nameIds.length; i++) { + String key = object.region.routeEncodingRules.get(object.nameIds[i]).getTag(); + String val = object.names.get(object.nameIds[i]); + if (key.startsWith("road_ref")) { + nameTag = key; + name = val; + } else { + additional.append(key).append("=").append(val).append(";"); + } + } +// LOG.debug("Additionals (names): " + additional.toString() ); + + Context context = topBar.getContext(); + int[] tps = object.getTypes(); + OsmandApplication app = ((OsmandApplication) context.getApplicationContext()); + RenderingRulesStorage storage = app.getRendererRegistry().getCurrentSelectedRenderer(); + boolean nightMode = app.getDaynightHelper().isNightMode(); + RenderingRuleSearchRequest rreq = map.getMyApplication().getResourceManager() + .getRenderer().getSearchRequestWithAppliedCustomRules(storage, nightMode); + + for (int i : tps) { + BinaryMapRouteReaderAdapter.RouteTypeRule tp = object.region.quickGetEncodingRule(i); + if (tp.getTag().equals("highway") || tp.getTag().equals("route")) { + rreq.setInitialTagValueZoom(tp.getTag(), tp.getValue(), 13, null); + } else { + additional.append(tp.getTag()).append("=").append(tp.getValue()).append(";"); + } + } + + rreq.setIntFilter(rreq.ALL.R_TEXT_LENGTH, name.length()); + rreq.setStringFilter(rreq.ALL.R_NAME_TAG, nameTag); + rreq.setStringFilter(rreq.ALL.R_ADDITIONAL, additional.toString()); + rreq.search(RenderingRulesStorage.TEXT_RULES); + + OsmandRenderer.RenderingContext rc = new OsmandRenderer.RenderingContext(context); + + TextRenderer textRenderer = new TextRenderer(context); + TextRenderer.TextDrawInfo text = new TextRenderer.TextDrawInfo(name); + + + Paint p = textRenderer.getPaintText(); + p.setTypeface(Typeface.create("Droid Serif", Typeface.BOLD)); + + int shieldRes = -1; + + if (rreq.isSpecified(rreq.ALL.R_TEXT_SHIELD)) { + text.setShieldResIcon(rreq.getStringPropertyValue(rreq.ALL.R_TEXT_SHIELD)); + shieldRes = app.getResources().getIdentifier("h_"+text.getShieldResIcon(), + "drawable", app.getPackageName()); + } + + if (rreq.isSpecified(rreq.ALL.R_TEXT_COLOR)) { + p.setColor(rreq.getIntPropertyValue(rreq.ALL.R_TEXT_COLOR)); + } + + if (rreq.isSpecified(rreq.ALL.R_TEXT_SIZE)) { + float ts = rreq.getFloatPropertyValue(rreq.ALL.R_TEXT_SIZE); + textRenderer.getPaintText().setTextSize( + TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, ts, + app.getResources().getDisplayMetrics())); + } + + if (shieldRes != -1) { + float xSize; + float ySize; + Bitmap shield; + shield = BitmapFactory.decodeResource(app.getResources(), shieldRes); + ySize = shield.getHeight(); + xSize = shield.getWidth(); + float xyRatio = xSize/ySize; + //setting view propotions (height is fixed by toolbar size - 48dp); + int viewHeightPx = AndroidUtils.dpToPx(context, 48); + int viewWidthPx = (int) (viewHeightPx * xyRatio); + + ViewGroup.LayoutParams params = view.getLayoutParams(); + params.width = viewWidthPx; + view.setLayoutParams(params); + + //creating bitmap according to size of resource + Bitmap bitmap = Bitmap.createBitmap((int) xSize, (int) ySize, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(bitmap); + text.fillProperties(rc, rreq, xSize/2, ySize/2 - p.getFontMetrics().ascent/2f); + textRenderer.drawShieldIcon(rc, canvas, text, text.getShieldResIcon()); + textRenderer.drawWrappedText(canvas, text, 20); + + view.setImageBitmap(bitmap); + return true; + } + return false; + } + public boolean updateWaypoint() { final LocationPointWrapper pnt = waypointHelper.getMostImportantLocationPoint(null); boolean changed = this.lastPoint != pnt; diff --git a/OsmAnd/src/net/osmand/plus/views/mapwidgets/RouteInfoWidgetsFactory.java b/OsmAnd/src/net/osmand/plus/views/mapwidgets/RouteInfoWidgetsFactory.java index 21615c55cb..366c215a48 100644 --- a/OsmAnd/src/net/osmand/plus/views/mapwidgets/RouteInfoWidgetsFactory.java +++ b/OsmAnd/src/net/osmand/plus/views/mapwidgets/RouteInfoWidgetsFactory.java @@ -875,9 +875,11 @@ public class RouteInfoWidgetsFactory { } - private static class LanesDrawable extends Drawable { - int[] lanes = null; + public static class LanesDrawable extends Drawable { + public int[] lanes = null; boolean imminent = false; + public boolean isTurnByTurn = false; + public boolean isNightMode = false; private Context ctx; private Paint paintBlack; private Paint paintRouteDirection; @@ -892,7 +894,7 @@ public class RouteInfoWidgetsFactory { private int imgMinDelta; private int imgMargin; - LanesDrawable(MapActivity ctx, float scaleCoefficent) { + public LanesDrawable(MapActivity ctx, float scaleCoefficent) { this.ctx = ctx; OsmandSettings settings = ctx.getMyApplication().getSettings(); leftSide = settings.DRIVING_REGION.get().leftHandDriving; @@ -916,7 +918,7 @@ public class RouteInfoWidgetsFactory { paintSecondTurn.setColor(ctx.getResources().getColor(R.color.nav_arrow_distant)); } - void updateBounds() { + public void updateBounds() { float w = 0; float h = 0; float delta = imgMinDelta; @@ -1031,8 +1033,13 @@ public class RouteInfoWidgetsFactory { // canvas.translate((int) (16 * scaleCoefficient), 0); for (int i = 0; i < lanes.length; i++) { if ((lanes[i] & 1) == 1) { - paintRouteDirection.setColor(imminent ? ctx.getResources().getColor(R.color.nav_arrow_imminent) : - ctx.getResources().getColor(R.color.nav_arrow)); + if (isTurnByTurn){ + paintRouteDirection.setColor(isNightMode ? ctx.getResources().getColor(R.color.active_color_primary_dark) : + ctx.getResources().getColor(R.color.active_color_primary_light)); + } else { + paintRouteDirection.setColor(imminent ? ctx.getResources().getColor(R.color.nav_arrow_imminent) : + ctx.getResources().getColor(R.color.nav_arrow)); + } } else { paintRouteDirection.setColor(ctx.getResources().getColor(R.color.nav_arrow_distant)); } diff --git a/OsmAnd/src/net/osmand/plus/voice/CommandBuilder.java b/OsmAnd/src/net/osmand/plus/voice/CommandBuilder.java index 2e7bdaf72a..65a28b70e6 100644 --- a/OsmAnd/src/net/osmand/plus/voice/CommandBuilder.java +++ b/OsmAnd/src/net/osmand/plus/voice/CommandBuilder.java @@ -39,6 +39,7 @@ public class CommandBuilder { protected static final String C_ATTENTION = "attention"; //$NON-NLS-1$ protected static final String C_OFF_ROUTE = "off_route"; //$NON-NLS-1$ protected static final String C_BACK_ON_ROUTE ="back_on_route"; //$NON-NLS-1$ + protected static final String C_TAKE_EXIT = "take_exit"; //$NON-NLS-1$ protected static final String C_BEAR_LEFT = "bear_left"; //$NON-NLS-1$ @@ -178,6 +179,14 @@ public class CommandBuilder { return alt(prepareStruct(C_TURN, param, dist, streetName), prepareStruct(C_TURN, param, dist)); } + public CommandBuilder takeExit(String turnType, StreetName streetName) { + return alt(prepareStruct(C_TAKE_EXIT, turnType, streetName), prepareStruct(C_TAKE_EXIT, turnType)); + } + + public CommandBuilder takeExit(String turnType, double dist, StreetName streetName) { + return alt(prepareStruct(C_TAKE_EXIT, turnType, dist, streetName), prepareStruct(C_TAKE_EXIT, turnType, dist)); + } + /** * * @param param A_LEFT, A_RIGHT, ... diff --git a/OsmAnd/src/net/osmand/plus/voice/JSCommandBuilder.java b/OsmAnd/src/net/osmand/plus/voice/JSCommandBuilder.java index f963eb81e6..dfdfcea29a 100644 --- a/OsmAnd/src/net/osmand/plus/voice/JSCommandBuilder.java +++ b/OsmAnd/src/net/osmand/plus/voice/JSCommandBuilder.java @@ -115,6 +115,14 @@ public class JSCommandBuilder extends CommandBuilder { return addCommand(C_TURN, param, dist, convertStreetName(streetName)); } + public JSCommandBuilder takeExit(String turnType, StreetName streetName) { + return takeExit(turnType, -1, streetName); + } + + public JSCommandBuilder takeExit(String turnType, double dist, StreetName streetName) { + return addCommand(C_TAKE_EXIT, turnType, dist, convertStreetName(streetName)); + } + /** * * @param param A_LEFT, A_RIGHT, ...