diff --git a/OsmAnd-java/src/main/java/net/osmand/router/RouteColorize.java b/OsmAnd-java/src/main/java/net/osmand/router/RouteColorize.java index 8fb0046bb4..0a39d57e25 100644 --- a/OsmAnd-java/src/main/java/net/osmand/router/RouteColorize.java +++ b/OsmAnd-java/src/main/java/net/osmand/router/RouteColorize.java @@ -1,9 +1,14 @@ package net.osmand.router; -import net.osmand.GPXUtilities; +import net.osmand.GPXUtilities.GPXFile; +import net.osmand.GPXUtilities.GPXTrackAnalysis; +import net.osmand.GPXUtilities.Track; +import net.osmand.GPXUtilities.TrkSegment; +import net.osmand.GPXUtilities.WptPt; import net.osmand.PlatformUtil; import net.osmand.osm.edit.Node; import net.osmand.osm.edit.OsmMapUtils; +import net.osmand.util.Algorithms; import net.osmand.util.MapUtils; import org.apache.commons.logging.Log; @@ -30,6 +35,8 @@ public class RouteColorize { public static final int RED = rgbaToDecimal(243, 55, 77, 255); public static final int[] colors = new int[] {GREEN, YELLOW, RED}; + private static final int MAX_SLOPE_VALUE = 25; + public enum ColorizationType { ELEVATION, SPEED, @@ -75,7 +82,11 @@ public class RouteColorize { /** * @param type ELEVATION, SPEED, SLOPE */ - public RouteColorize(int zoom, GPXUtilities.GPXFile gpxFile, ColorizationType type) { + public RouteColorize(int zoom, GPXFile gpxFile, ColorizationType type) { + this(zoom, gpxFile, null, type, 0); + } + + public RouteColorize(int zoom, GPXFile gpxFile, GPXTrackAnalysis analysis, ColorizationType type, float maxProfileSpeed) { if (!gpxFile.hasTrkPt()) { LOG.warn("GPX file is not consist of track points"); @@ -85,21 +96,30 @@ public class RouteColorize { List latList = new ArrayList<>(); List lonList = new ArrayList<>(); List valList = new ArrayList<>(); - for (GPXUtilities.Track t : gpxFile.tracks) { - for (GPXUtilities.TrkSegment ts : t.segments) { - for (GPXUtilities.WptPt p : ts.points) { + int wptIdx = 0; + + if (analysis == null) { + analysis = Algorithms.isEmpty(gpxFile.path) + ? gpxFile.getAnalysis(System.currentTimeMillis()) + : gpxFile.getAnalysis(gpxFile.modifiedTime); + } + for (Track t : gpxFile.tracks) { + for (TrkSegment ts : t.segments) { + for (WptPt p : ts.points) { latList.add(p.lat); lonList.add(p.lon); if (type == ColorizationType.SPEED) { - valList.add(p.speed); + valList.add((double) analysis.speedData.get(wptIdx).speed); } else { - valList.add(p.ele); + valList.add((double) analysis.elevationData.get(wptIdx).elevation); } + wptIdx++; } } } this.zoom = zoom; + colorizationType = type; latitudes = listToArray(latList); longitudes = listToArray(lonList); @@ -108,9 +128,8 @@ public class RouteColorize { } else { values = listToArray(valList); } - calculateMinMaxValue(); - colorizationType = type; + maxValue = getMaxValue(colorizationType, analysis, minValue, maxProfileSpeed); checkPalette(); sortPalette(); } @@ -194,7 +213,7 @@ public class RouteColorize { return rgbaToDecimal((int) resultRed, (int) resultGreen, (int) resultBlue, (int) resultAlpha); } } - return getDefaultColor(); + return getTransparentColor(); } public void setPalette(double[][] palette) { @@ -209,12 +228,12 @@ public class RouteColorize { } setPalette(new double[][] { {minValue, gradientPalette[0]}, - {colorizationType == ColorizationType.SLOPE ? 0 : (minValue + maxValue) / 2, gradientPalette[1]}, + {(minValue + maxValue) / 2, gradientPalette[1]}, {maxValue, gradientPalette[2]} }); } - private int getDefaultColor() { + private int getTransparentColor() { return rgbaToDecimal(0, 0, 0, 0); } @@ -295,7 +314,7 @@ public class RouteColorize { double[][] defaultPalette = { {minValue, GREEN}, - {colorizationType == ColorizationType.SLOPE ? 0 : (minValue + maxValue) / 2, YELLOW}, + {(minValue + maxValue) / 2, YELLOW}, {maxValue, RED} }; palette = defaultPalette; @@ -397,6 +416,20 @@ public class RouteColorize { return result; } + public static double getMinValue(ColorizationType type, GPXTrackAnalysis analysis) { + return type == ColorizationType.ELEVATION ? analysis.minElevation : 0.0; + } + + public static double getMaxValue(ColorizationType type, GPXTrackAnalysis analysis, double minValue, double maxProfileSpeed) { + if (type == ColorizationType.SPEED) { + return Math.max(analysis.maxSpeed, maxProfileSpeed); + } else if (type == ColorizationType.ELEVATION) { + return Math.max(analysis.maxElevation, minValue + 50); + } else { + return MAX_SLOPE_VALUE; + } + } + private void calculateMinMaxValue() { if (values.length == 0) return; @@ -457,5 +490,4 @@ public class RouteColorize { this.val = val; } } - -} +} \ No newline at end of file diff --git a/OsmAnd/res/layout/profile_preference_toolbar_with_switch.xml b/OsmAnd/res/layout/profile_preference_toolbar_with_switch.xml index 33a93fc70e..c9ddd04254 100644 --- a/OsmAnd/res/layout/profile_preference_toolbar_with_switch.xml +++ b/OsmAnd/res/layout/profile_preference_toolbar_with_switch.xml @@ -5,10 +5,10 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="0dp" - osmand:contentInsetLeft="0dp" - osmand:contentInsetStart="0dp" - osmand:contentInsetRight="0dp" osmand:contentInsetEnd="0dp" + osmand:contentInsetLeft="0dp" + osmand:contentInsetRight="0dp" + osmand:contentInsetStart="0dp" osmand:theme="@style/ThemeOverlay.AppCompat.ActionBar"> + android:tint="?attr/default_icon_color" + osmand:srcCompat="@drawable/ic_arrow_back" /> + android:orientation="vertical" + android:paddingTop="@dimen/content_padding_half" + android:paddingBottom="@dimen/content_padding_half"> Non-immigrant visas Immigrant visas Citizen services + Bay type Store @@ -284,7 +285,6 @@ Ship chandler Sporting goods Stationery store - Tableware store Ticket sales Tobacco store Toyshop @@ -4379,4 +4379,6 @@ Horseshoes Kickboxing + Diplomatic office + diff --git a/OsmAnd/res/values/strings.xml b/OsmAnd/res/values/strings.xml index 7e282f5171..db67d2bf0d 100644 --- a/OsmAnd/res/values/strings.xml +++ b/OsmAnd/res/values/strings.xml @@ -12,6 +12,11 @@ --> + %1$s → … + Output + User points + Announce when exceeded + Exit number Please select the needed format. You will need to re-download the file to change the format. OsmAnd provides contour lines data in meters and feet. You will need to re-download the file to change the format. Contour lines unit format @@ -2480,7 +2485,7 @@ Traffic calming Speed camera Traffic warning - Favorites nearby + Nearby favorites Nearby POI Traffic warnings OsmAnd background service still running. Stop it, too? diff --git a/OsmAnd/res/xml/voice_announces.xml b/OsmAnd/res/xml/voice_announces.xml index a7dfad299b..51ce54c40b 100644 --- a/OsmAnd/res/xml/voice_announces.xml +++ b/OsmAnd/res/xml/voice_announces.xml @@ -11,11 +11,41 @@ android:title="@string/voice_announces_info" tools:icon="@drawable/ic_action_info_dark" /> + + + + + + + + + + + + - - - - - - - - - - + + + + + + + + + + + + + + + + - - + android:title="@string/output" /> diff --git a/OsmAnd/src/net/osmand/AndroidUtils.java b/OsmAnd/src/net/osmand/AndroidUtils.java index 42b4a60919..4f0729d355 100644 --- a/OsmAnd/src/net/osmand/AndroidUtils.java +++ b/OsmAnd/src/net/osmand/AndroidUtils.java @@ -151,6 +151,10 @@ public class AndroidUtils { return resizedBitmap; } + public static Bitmap createScaledBitmap(Drawable drawable, int width, int height) { + return scaleBitmap(drawableToBitmap(drawable), width, height, false); + } + public static ColorStateList createBottomNavColorStateList(Context ctx, boolean nightMode) { return AndroidUtils.createCheckedColorStateList(ctx, nightMode, R.color.icon_color_default_light, R.color.wikivoyage_active_light, diff --git a/OsmAnd/src/net/osmand/plus/activities/MapActivityKeyListener.java b/OsmAnd/src/net/osmand/plus/activities/MapActivityKeyListener.java index 46ce19e3c2..08004ec9ab 100644 --- a/OsmAnd/src/net/osmand/plus/activities/MapActivityKeyListener.java +++ b/OsmAnd/src/net/osmand/plus/activities/MapActivityKeyListener.java @@ -92,6 +92,8 @@ public class MapActivityKeyListener implements KeyEvent.Callback { return true; } else if (keyCode == KeyEvent.KEYCODE_C) { mapActivity.getMapViewTrackingUtilities().backToLocationImpl(); + } else if (keyCode == KeyEvent.KEYCODE_D) { + mapActivity.getMapViewTrackingUtilities().switchRotateMapMode(); } else if (settings.EXTERNAL_INPUT_DEVICE.get() == PARROT_EXTERNAL_DEVICE) { // Parrot device has only dpad left and right if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) { diff --git a/OsmAnd/src/net/osmand/plus/base/MapViewTrackingUtilities.java b/OsmAnd/src/net/osmand/plus/base/MapViewTrackingUtilities.java index 14a6623eae..d220b8bec1 100644 --- a/OsmAnd/src/net/osmand/plus/base/MapViewTrackingUtilities.java +++ b/OsmAnd/src/net/osmand/plus/base/MapViewTrackingUtilities.java @@ -39,6 +39,8 @@ import java.util.Map; public class MapViewTrackingUtilities implements OsmAndLocationListener, IMapLocationListener, OsmAndCompassListener, MapMarkerChangedListener { + + private static final int COMPASS_REQUEST_TIME_INTERVAL_MS = 5000; private static final int AUTO_FOLLOW_MSG_ID = OsmAndConstants.UI_HANDLER_LOCATION_SERVICE + 4; private long lastTimeAutoZooming = 0; @@ -59,6 +61,7 @@ public class MapViewTrackingUtilities implements OsmAndLocationListener, IMapLoc private Float heading; private boolean drivingRegionUpdated = false; private boolean movingToMyLocation = false; + private long compassRequest; public MapViewTrackingUtilities(OsmandApplication app){ this.app = app; @@ -424,7 +427,22 @@ public class MapViewTrackingUtilities implements OsmAndLocationListener, IMapLoc setMapLinkedToLocation(false); } - public void switchRotateMapMode(){ + public void switchRotateMapMode() { + if (app.getRoutingHelper().isFollowingMode()) { + if (compassRequest + COMPASS_REQUEST_TIME_INTERVAL_MS > System.currentTimeMillis()) { + compassRequest = 0; + switchRotateMapModeImpl(); + } else { + compassRequest = System.currentTimeMillis(); + app.showShortToastMessage(app.getString(R.string.press_again_to_change_the_map_orientation)); + } + } else { + compassRequest = 0; + switchRotateMapModeImpl(); + } + } + + private void switchRotateMapModeImpl(){ if (mapView != null) { String rotMode = app.getString(R.string.rotate_map_none_opt); if (settings.ROTATE_MAP.get() == OsmandSettings.ROTATE_MAP_NONE && mapView.getRotate() != 0) { diff --git a/OsmAnd/src/net/osmand/plus/measurementtool/MeasurementEditingContext.java b/OsmAnd/src/net/osmand/plus/measurementtool/MeasurementEditingContext.java index 3512de68bc..af4d599afa 100644 --- a/OsmAnd/src/net/osmand/plus/measurementtool/MeasurementEditingContext.java +++ b/OsmAnd/src/net/osmand/plus/measurementtool/MeasurementEditingContext.java @@ -2,9 +2,6 @@ package net.osmand.plus.measurementtool; import android.util.Pair; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - import net.osmand.GPXUtilities.GPXFile; import net.osmand.GPXUtilities.TrkSegment; import net.osmand.GPXUtilities.WptPt; @@ -38,12 +35,18 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import static net.osmand.plus.measurementtool.MeasurementEditingContext.CalculationMode.NEXT_SEGMENT; import static net.osmand.plus.measurementtool.MeasurementEditingContext.CalculationMode.WHOLE_TRACK; import static net.osmand.plus.measurementtool.command.MeasurementModeCommand.MeasurementCommandType.APPROXIMATE_POINTS; @@ -1111,8 +1114,26 @@ public class MeasurementEditingContext implements IRouteSettingsListener { return res; } + public boolean isInMultiProfileMode() { + if (lastCalculationMode == CalculationMode.NEXT_SEGMENT) { + return true; + } + Set profiles = new HashSet<>(); + for (RoadSegmentData segmentData : roadSegmentData.values()) { + String profile = segmentData.getAppMode().getStringKey(); + if (!DEFAULT_APP_MODE.getStringKey().equals(profile)) { + profiles.add(profile); + if (profiles.size() >= 2) { + lastCalculationMode = NEXT_SEGMENT; + return true; + } + } + } + return false; + } + @Override public void onRouteSettingsChanged(@Nullable ApplicationMode mode) { recalculateRouteSegments(mode); } -} +} \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/measurementtool/MeasurementToolLayer.java b/OsmAnd/src/net/osmand/plus/measurementtool/MeasurementToolLayer.java index 3c6bcf6c89..9d3f8812c4 100644 --- a/OsmAnd/src/net/osmand/plus/measurementtool/MeasurementToolLayer.java +++ b/OsmAnd/src/net/osmand/plus/measurementtool/MeasurementToolLayer.java @@ -23,6 +23,8 @@ import net.osmand.plus.views.OsmandMapTileView; import net.osmand.plus.views.Renderable; import net.osmand.plus.views.layers.ContextMenuLayer; import net.osmand.plus.views.layers.geometry.GeometryWay; +import net.osmand.plus.views.layers.geometry.MultiProfileGeometryWay; +import net.osmand.plus.views.layers.geometry.MultiProfileGeometryWayContext; import net.osmand.util.MapUtils; import java.util.ArrayList; @@ -33,20 +35,28 @@ public class MeasurementToolLayer extends OsmandMapLayer implements ContextMenuL private OsmandMapTileView view; private boolean inMeasurementMode; + private Bitmap centerIconDay; private Bitmap centerIconNight; private Bitmap pointIcon; private Bitmap applyingPointIcon; private Paint bitmapPaint; private final RenderingLineAttributes lineAttrs = new RenderingLineAttributes("measureDistanceLine"); + private final RenderingLineAttributes multiProfileLineAttrs = new RenderingLineAttributes("multiProfileMeasureDistanceLine"); + + private MultiProfileGeometryWay multiProfileGeometry; + private MultiProfileGeometryWayContext multiProfileGeometryWayContext; + private int marginPointIconX; private int marginPointIconY; private int marginApplyingPointIconX; private int marginApplyingPointIconY; private final Path path = new Path(); + private final List tx = new ArrayList<>(); private final List ty = new ArrayList<>(); private OnMeasureDistanceToCenter measureDistanceToCenterListener; + private OnSingleTapListener singleTapListener; private OnEnterMovePointModeListener enterMovePointModeListener; private LatLon pressedPointLatLon; @@ -63,6 +73,19 @@ public class MeasurementToolLayer extends OsmandMapLayer implements ContextMenuL pointIcon = BitmapFactory.decodeResource(view.getResources(), R.drawable.map_measure_point_day); applyingPointIcon = BitmapFactory.decodeResource(view.getResources(), R.drawable.map_measure_point_move_day); + float density = view.getDensity(); + multiProfileLineAttrs.isPaint_1 = false; + multiProfileLineAttrs.paint_1.setColor(0xFFFFFFFF); + multiProfileLineAttrs.paint_1.setStyle(Paint.Style.FILL); + multiProfileLineAttrs.paint.setStrokeWidth(density * 14); + multiProfileLineAttrs.paint2.setStrokeWidth(density * 10); + multiProfileLineAttrs.isPaint3 = false; + multiProfileLineAttrs.paint3.setStrokeWidth(density * 2); + + multiProfileGeometryWayContext = new MultiProfileGeometryWayContext( + view.getContext(), view.getApplication().getUIUtilities(), density); + multiProfileGeometry = new MultiProfileGeometryWay(multiProfileGeometryWayContext); + bitmapPaint = new Paint(); bitmapPaint.setAntiAlias(true); bitmapPaint.setDither(true); @@ -194,15 +217,23 @@ public class MeasurementToolLayer extends OsmandMapLayer implements ContextMenuL } } - List before = editingCtx.getBeforeTrkSegmentLine(); - for (TrkSegment segment : before) { - new Renderable.StandardTrack(new ArrayList<>(segment.points), 17.2). - drawSegment(view.getZoom(), lineAttrs.paint, canvas, tb); - } - List after = editingCtx.getAfterTrkSegmentLine(); - for (TrkSegment segment : after) { - new Renderable.StandardTrack(new ArrayList<>(segment.points), 17.2). - drawSegment(view.getZoom(), lineAttrs.paint, canvas, tb); + if (editingCtx.isInMultiProfileMode()) { + multiProfileGeometryWayContext.updatePaints(settings.isNightMode(), multiProfileLineAttrs); + multiProfileGeometry.updateRoute(tb, editingCtx.getRoadSegmentData(), editingCtx.getBeforeSegments(), editingCtx.getAfterSegments()); + multiProfileGeometry.drawSegments(canvas, tb); + } else { + multiProfileGeometry.clearWay(); + List before = editingCtx.getBeforeTrkSegmentLine(); + for (TrkSegment segment : before) { + new Renderable.StandardTrack(new ArrayList<>(segment.points), 17.2). + drawSegment(view.getZoom(), lineAttrs.paint, canvas, tb); + } + + List after = editingCtx.getAfterTrkSegmentLine(); + for (TrkSegment segment : after) { + new Renderable.StandardTrack(new ArrayList<>(segment.points), 17.2). + drawSegment(view.getZoom(), lineAttrs.paint, canvas, tb); + } } drawPoints(canvas, tb); @@ -237,10 +268,10 @@ public class MeasurementToolLayer extends OsmandMapLayer implements ContextMenuL List beforePoints = editingCtx.getBeforePoints(); List afterPoints = editingCtx.getAfterPoints(); if (beforePoints.size() > 0) { - drawPointIcon(canvas, tb, beforePoints.get(beforePoints.size() - 1)); + drawPointIcon(canvas, tb, beforePoints.get(beforePoints.size() - 1), true); } if (afterPoints.size() > 0) { - drawPointIcon(canvas, tb, afterPoints.get(0)); + drawPointIcon(canvas, tb, afterPoints.get(0), true); } if (editingCtx.getSelectedPointPosition() != -1) { @@ -287,25 +318,13 @@ public class MeasurementToolLayer extends OsmandMapLayer implements ContextMenuL } if (overlapped) { WptPt pt = points.get(0); - if (pt != lastBeforePoint && pt != firstAfterPoint && isInTileBox(tb, pt)) { - float locX = tb.getPixXFromLatLon(pt.lat, pt.lon); - float locY = tb.getPixYFromLatLon(pt.lat, pt.lon); - canvas.drawBitmap(pointIcon, locX - marginPointIconX, locY - marginPointIconY, bitmapPaint); - } + drawPointIcon(canvas, tb, pt, false); pt = points.get(points.size() - 1); - if (pt != lastBeforePoint && pt != firstAfterPoint && isInTileBox(tb, pt)) { - float locX = tb.getPixXFromLatLon(pt.lat, pt.lon); - float locY = tb.getPixYFromLatLon(pt.lat, pt.lon); - canvas.drawBitmap(pointIcon, locX - marginPointIconX, locY - marginPointIconY, bitmapPaint); - } + drawPointIcon(canvas, tb, pt, false); } else { for (int i = 0; i < points.size(); i++) { WptPt pt = points.get(i); - if (pt != lastBeforePoint && pt != firstAfterPoint && isInTileBox(tb, pt)) { - float locX = tb.getPixXFromLatLon(pt.lat, pt.lon); - float locY = tb.getPixYFromLatLon(pt.lat, pt.lon); - canvas.drawBitmap(pointIcon, locX - marginPointIconX, locY - marginPointIconY, bitmapPaint); - } + drawPointIcon(canvas, tb, pt, false); } } @@ -370,14 +389,23 @@ public class MeasurementToolLayer extends OsmandMapLayer implements ContextMenuL canvas.rotate(tb.getRotate(), tb.getCenterPixelX(), tb.getCenterPixelY()); } - private void drawPointIcon(Canvas canvas, RotatedTileBox tb, WptPt pt) { - canvas.rotate(-tb.getRotate(), tb.getCenterPixelX(), tb.getCenterPixelY()); + private void drawPointIcon(Canvas canvas, RotatedTileBox tb, WptPt pt, boolean rotate) { + if (rotate) { + canvas.rotate(-tb.getRotate(), tb.getCenterPixelX(), tb.getCenterPixelY()); + } float locX = tb.getPixXFromLatLon(pt.lat, pt.lon); float locY = tb.getPixYFromLatLon(pt.lat, pt.lon); - if (tb.containsPoint(locX, locY, 0)) { - canvas.drawBitmap(pointIcon, locX - marginPointIconX, locY - marginPointIconY, bitmapPaint); + if (editingCtx.isInMultiProfileMode()) { + canvas.drawBitmap(multiProfileGeometryWayContext.getPointIcon(), locX - multiProfileGeometryWayContext.pointIconSize / 2, + locY - multiProfileGeometryWayContext.pointIconSize / 2, bitmapPaint); + } else { + if (tb.containsPoint(locX, locY, 0)) { + canvas.drawBitmap(pointIcon, locX - marginPointIconX, locY - marginPointIconY, bitmapPaint); + } + } + if (rotate) { + canvas.rotate(tb.getRotate(), tb.getCenterPixelX(), tb.getCenterPixelY()); } - canvas.rotate(tb.getRotate(), tb.getCenterPixelX(), tb.getCenterPixelY()); } public WptPt addCenterPoint(boolean addPointBefore) { @@ -509,4 +537,4 @@ public class MeasurementToolLayer extends OsmandMapLayer implements ContextMenuL interface OnMeasureDistanceToCenter { void onMeasure(float distance, float bearing); } -} +} \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/quickaction/actions/MapStyleAction.java b/OsmAnd/src/net/osmand/plus/quickaction/actions/MapStyleAction.java index 1bcac88a36..5838bfc1a2 100644 --- a/OsmAnd/src/net/osmand/plus/quickaction/actions/MapStyleAction.java +++ b/OsmAnd/src/net/osmand/plus/quickaction/actions/MapStyleAction.java @@ -245,4 +245,11 @@ public class MapStyleAction extends SwitchableAction { ? filters.get(0) + " +" + (filters.size() - 1) : filters.get(0); } + + @Override + public String getActionText(OsmandApplication application) { + String currentSource = application.getSettings().RENDERER.get(); + + return application.getString(R.string.map_quick_action_pattern, getTranslatedItemName(application, currentSource)); + } } \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/rastermaps/MapOverlayAction.java b/OsmAnd/src/net/osmand/plus/rastermaps/MapOverlayAction.java index 7972b264aa..8190b6c995 100644 --- a/OsmAnd/src/net/osmand/plus/rastermaps/MapOverlayAction.java +++ b/OsmAnd/src/net/osmand/plus/rastermaps/MapOverlayAction.java @@ -104,8 +104,8 @@ public class MapOverlayAction extends SwitchableAction> { } int index = -1; - final String currentSource = settings.MAP_OVERLAY.get() == null ? KEY_NO_OVERLAY - : settings.MAP_OVERLAY.get(); + String currentSource = settings.MAP_OVERLAY.get() == null ? KEY_NO_OVERLAY + : settings.MAP_OVERLAY.get(); for (int idx = 0; idx < sources.size(); idx++) { if (sources.get(idx).first.equals(currentSource)) { @@ -224,4 +224,12 @@ public class MapOverlayAction extends SwitchableAction> { getParams().put(KEY_DIALOG, Boolean.toString(((SwitchCompat) root.findViewById(R.id.saveButton)).isChecked())); return super.fillParams(root, activity); } + + @Override + public String getActionText(OsmandApplication application) { + String currentSource = application.getSettings().MAP_OVERLAY.get() == null ? KEY_NO_OVERLAY + : application.getSettings().MAP_OVERLAY.get(); + + return application.getString(R.string.map_quick_action_pattern, getTranslatedItemName(application, currentSource)); + } } diff --git a/OsmAnd/src/net/osmand/plus/rastermaps/MapSourceAction.java b/OsmAnd/src/net/osmand/plus/rastermaps/MapSourceAction.java index b11a8ec812..d81b1f12ed 100644 --- a/OsmAnd/src/net/osmand/plus/rastermaps/MapSourceAction.java +++ b/OsmAnd/src/net/osmand/plus/rastermaps/MapSourceAction.java @@ -91,7 +91,7 @@ public class MapSourceAction extends SwitchableAction> { OsmandSettings settings = activity.getMyApplication().getSettings(); List> sources = loadListFromParams(); if (sources.size() > 0) { - boolean showBottomSheetStyles = Boolean.valueOf(getParams().get(KEY_DIALOG)); + boolean showBottomSheetStyles = Boolean.parseBoolean(getParams().get(KEY_DIALOG)); if (showBottomSheetStyles) { showChooseDialog(activity.getSupportFragmentManager()); return; @@ -214,4 +214,13 @@ public class MapSourceAction extends SwitchableAction> { getParams().put(KEY_DIALOG, Boolean.toString(((SwitchCompat) root.findViewById(R.id.saveButton)).isChecked())); return super.fillParams(root, activity); } + + @Override + public String getActionText(OsmandApplication application) { + String currentSource = application.getSettings().MAP_ONLINE_DATA.get() + ? application.getSettings().MAP_TILE_SOURCES.get() + : application.getString(R.string.vector_data); + + return application.getString(R.string.map_quick_action_pattern, getTranslatedItemName(application, currentSource)); + } } diff --git a/OsmAnd/src/net/osmand/plus/rastermaps/MapUnderlayAction.java b/OsmAnd/src/net/osmand/plus/rastermaps/MapUnderlayAction.java index ed598a344b..ddb941e10b 100644 --- a/OsmAnd/src/net/osmand/plus/rastermaps/MapUnderlayAction.java +++ b/OsmAnd/src/net/osmand/plus/rastermaps/MapUnderlayAction.java @@ -226,4 +226,12 @@ public class MapUnderlayAction extends SwitchableAction> { getParams().put(KEY_DIALOG, Boolean.toString(((SwitchCompat) root.findViewById(R.id.saveButton)).isChecked())); return super.fillParams(root, activity); } + + @Override + public String getActionText(OsmandApplication application) { + String currentSource = application.getSettings().MAP_UNDERLAY.get() == null ? KEY_NO_UNDERLAY + : application.getSettings().MAP_UNDERLAY.get(); + + return application.getString(R.string.map_quick_action_pattern, getTranslatedItemName(application, currentSource)); + } } diff --git a/OsmAnd/src/net/osmand/plus/routing/VoiceRouter.java b/OsmAnd/src/net/osmand/plus/routing/VoiceRouter.java index b9032e26e4..290ff960c8 100644 --- a/OsmAnd/src/net/osmand/plus/routing/VoiceRouter.java +++ b/OsmAnd/src/net/osmand/plus/routing/VoiceRouter.java @@ -700,7 +700,7 @@ public class VoiceRouter { boolean isPlay = true; ExitInfo exitInfo = next.getExitInfo(); if (tParam != null) { - if (exitInfo != null && !Algorithms.isEmpty(exitInfo.getRef())) { + if (exitInfo != null && !Algorithms.isEmpty(exitInfo.getRef()) && settings.SPEAK_EXIT_NUMBER_NAMES.get()) { String stringRef = getSpeakableExitRef(exitInfo.getRef()); p.takeExit(tParam, dist, stringRef, getIntRef(exitInfo.getRef()), getSpeakableExitName(next, exitInfo, true)); } else { @@ -775,7 +775,7 @@ public class VoiceRouter { ExitInfo exitInfo = next.getExitInfo(); boolean isplay = true; if (tParam != null) { - if (exitInfo != null && !Algorithms.isEmpty(exitInfo.getRef())) { + if (exitInfo != null && !Algorithms.isEmpty(exitInfo.getRef()) && settings.SPEAK_EXIT_NUMBER_NAMES.get()) { String stringRef = getSpeakableExitRef(exitInfo.getRef()); p.takeExit(tParam, stringRef, getIntRef(exitInfo.getRef()), getSpeakableExitName(next, exitInfo, !suppressDest)); } else { diff --git a/OsmAnd/src/net/osmand/plus/routing/cards/RouteLineWidthCard.java b/OsmAnd/src/net/osmand/plus/routing/cards/RouteLineWidthCard.java index 72055da936..d9843f72ea 100644 --- a/OsmAnd/src/net/osmand/plus/routing/cards/RouteLineWidthCard.java +++ b/OsmAnd/src/net/osmand/plus/routing/cards/RouteLineWidthCard.java @@ -36,7 +36,7 @@ import java.util.List; public class RouteLineWidthCard extends BaseCard { private final static int CUSTOM_WIDTH_MIN = 1; - private final static int CUSTOM_WIDTH_MAX = 24; + private final static int CUSTOM_WIDTH_MAX = 36; private RouteLineDrawInfo routeLineDrawInfo; private OnNeedScrollListener onNeedScrollListener; diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/OsmandSettings.java b/OsmAnd/src/net/osmand/plus/settings/backend/OsmandSettings.java index f227cb9b1e..501d84d608 100644 --- a/OsmAnd/src/net/osmand/plus/settings/backend/OsmandSettings.java +++ b/OsmAnd/src/net/osmand/plus/settings/backend/OsmandSettings.java @@ -1355,6 +1355,7 @@ public class OsmandSettings { public final OsmandPreference SPEAK_SPEED_LIMIT = new BooleanPreference(this, "speak_speed_limit", false).makeProfile().cache(); public final OsmandPreference SPEAK_SPEED_CAMERA = new BooleanPreference(this, "speak_cameras", false).makeProfile().cache(); public final OsmandPreference SPEAK_TUNNELS = new BooleanPreference(this, "speak_tunnels", false).makeProfile().cache(); + public final OsmandPreference SPEAK_EXIT_NUMBER_NAMES = new BooleanPreference(this, "exit_number_names", true).makeProfile().cache(); public final OsmandPreference SPEED_CAMERAS_UNINSTALLED = new BooleanPreference(this, "speed_cameras_uninstalled", false).makeGlobal().makeShared(); public final OsmandPreference SPEED_CAMERAS_ALERT_SHOWED = new BooleanPreference(this, "speed_cameras_alert_showed", false).makeGlobal().makeShared(); diff --git a/OsmAnd/src/net/osmand/plus/settings/datastorage/DataStorageFragment.java b/OsmAnd/src/net/osmand/plus/settings/datastorage/DataStorageFragment.java index 41c58ebd7f..57da72c4cc 100644 --- a/OsmAnd/src/net/osmand/plus/settings/datastorage/DataStorageFragment.java +++ b/OsmAnd/src/net/osmand/plus/settings/datastorage/DataStorageFragment.java @@ -234,7 +234,7 @@ public class DataStorageFragment extends BaseSettingsFragment implements DataSto ivIcon.setImageDrawable(icon); if (currentKey.equals(MANUALLY_SPECIFIED)) { - tvSummary.setText(item.getDirectory()); + setFormattedPath(item, tvSummary); secondPart.setVisibility(View.GONE); tvAdditionalDescription.setVisibility(View.GONE); divider.setVisibility(View.GONE); @@ -252,15 +252,8 @@ public class DataStorageFragment extends BaseSettingsFragment implements DataSto } if (currentKey.equals(INTERNAL_STORAGE)) { tvAdditionalDescription.setText(item.getDescription()); - } else if (currentKey.equals(SHARED_STORAGE)) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { - BidiFormatter rtlFormatter = BidiFormatter.getInstance(); - tvAdditionalDescription.setText(rtlFormatter.unicodeWrap(item.getDirectory())); - } else { - tvAdditionalDescription.setText(String.format("\u200E%s", item.getDirectory())); - } } else { - tvAdditionalDescription.setText(item.getDirectory()); + setFormattedPath(item, tvAdditionalDescription); } } } @@ -310,6 +303,15 @@ public class DataStorageFragment extends BaseSettingsFragment implements DataSto } } + private void setFormattedPath(StorageItem item, TextView tvAdditionalDescription) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { + BidiFormatter pathRtlFormatter = BidiFormatter.getInstance(); + tvAdditionalDescription.setText(pathRtlFormatter.unicodeWrap(item.getDirectory())); + } else { + tvAdditionalDescription.setText(String.format("\u200E%s", item.getDirectory())); + } + } + @Override public void onDestroy() { if (!activity.isChangingConfigurations()) { diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/BaseSettingsFragment.java b/OsmAnd/src/net/osmand/plus/settings/fragments/BaseSettingsFragment.java index 6eeea74426..05c4c7e323 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/BaseSettingsFragment.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/BaseSettingsFragment.java @@ -924,12 +924,6 @@ public abstract class BaseSettingsFragment extends PreferenceFragmentCompat impl } } - public void setupSpeedCamerasAlert() { - Preference speedCamerasAlert = findPreference(settings.SPEED_CAMERAS_UNINSTALLED.getId()); - speedCamerasAlert.setIcon(getContentIcon(R.drawable.ic_action_alert)); - speedCamerasAlert.setVisible(!settings.SPEED_CAMERAS_UNINSTALLED.get()); - } - public void setupPrefRoundedBg(PreferenceViewHolder holder) { View selectableView = holder.itemView.findViewById(R.id.selectable_list_item); if (selectableView != null) { diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/RouteLineAppearanceFragment.java b/OsmAnd/src/net/osmand/plus/settings/fragments/RouteLineAppearanceFragment.java index 2975815bb4..254e1e3d3d 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/RouteLineAppearanceFragment.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/RouteLineAppearanceFragment.java @@ -15,6 +15,7 @@ import android.widget.ScrollView; import androidx.activity.OnBackPressedCallback; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.core.content.ContextCompat; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentActivity; @@ -32,7 +33,6 @@ import net.osmand.plus.routing.cards.RouteLineColorCard; import net.osmand.plus.routing.cards.RouteLineColorCard.OnMapThemeUpdateListener; import net.osmand.plus.routing.cards.RouteLineColorCard.OnSelectedColorChangeListener; import net.osmand.plus.routing.cards.RouteLineWidthCard; -import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.track.CustomColorBottomSheet.ColorPickerListener; import net.osmand.plus.track.TrackAppearanceFragment.OnNeedScrollListener; @@ -203,6 +203,8 @@ public class RouteLineAppearanceFragment extends ContextMenuScrollFragment imple } }); closeButton.setImageResource(AndroidUtils.getNavigationIconResId(toolbarContainer.getContext())); + int bgColorId = isNightMode() ? R.color.app_bar_color_dark : R.color.list_background_color_light; + toolbarContainer.setBackgroundColor(ContextCompat.getColor(requireContext(), bgColorId)); updateToolbarVisibility(toolbarContainer); } @@ -212,7 +214,7 @@ public class RouteLineAppearanceFragment extends ContextMenuScrollFragment imple if (Build.VERSION.SDK_INT >= 23 && !isNightMode() && view != null) { view.setSystemUiVisibility(view.getSystemUiVisibility() | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); } - return isNightMode() ? R.color.divider_color_dark : R.color.divider_color_light; + return isNightMode() ? R.color.status_bar_color_dark : R.color.divider_color_light; } @Override diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/ScreenAlertsFragment.java b/OsmAnd/src/net/osmand/plus/settings/fragments/ScreenAlertsFragment.java index a312253aee..b10d149342 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/ScreenAlertsFragment.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/ScreenAlertsFragment.java @@ -164,4 +164,10 @@ public class ScreenAlertsFragment extends BaseSettingsFragment implements OnPref showCameras.setIcon(getIcon(R.drawable.list_warnings_speed_camera)); showCameras.setVisible(!settings.SPEED_CAMERAS_UNINSTALLED.get()); } + + public void setupSpeedCamerasAlert() { + Preference speedCamerasAlert = findPreference(settings.SPEED_CAMERAS_UNINSTALLED.getId()); + speedCamerasAlert.setIcon(getContentIcon(R.drawable.ic_action_alert)); + speedCamerasAlert.setVisible(!settings.SPEED_CAMERAS_UNINSTALLED.get()); + } } \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/VoiceAnnouncesFragment.java b/OsmAnd/src/net/osmand/plus/settings/fragments/VoiceAnnouncesFragment.java index fb617ab01f..f041572b91 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/VoiceAnnouncesFragment.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/VoiceAnnouncesFragment.java @@ -5,6 +5,7 @@ import android.content.Intent; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.media.AudioManager; +import android.net.Uri; import android.view.LayoutInflater; import android.view.View; import android.widget.ImageView; @@ -20,7 +21,6 @@ import androidx.preference.SwitchPreferenceCompat; import net.osmand.AndroidUtils; import net.osmand.plus.R; import net.osmand.plus.UiUtilities; -import net.osmand.plus.Version; import net.osmand.plus.activities.MapActivity; import net.osmand.plus.dialogs.SpeedCamerasBottomSheet; import net.osmand.plus.download.DownloadActivity; @@ -31,6 +31,8 @@ import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.settings.bottomsheets.AnnouncementTimeBottomSheet; import net.osmand.plus.settings.preferences.ListPreferenceEx; +import net.osmand.plus.settings.preferences.SwitchPreferenceEx; +import net.osmand.plus.wikipedia.WikipediaDialogFragment; import java.util.Set; @@ -43,6 +45,8 @@ public class VoiceAnnouncesFragment extends BaseSettingsFragment implements OnPr private static final String MORE_VALUE = "MORE_VALUE"; + private static final String OSMAND_VOICE_NAVIGATION_URL = "https://docs.osmand.net/en/main@latest/osmand/troubleshooting/navigation#voice-navigation"; + @Override protected void createToolbar(LayoutInflater inflater, View view) { super.createToolbar(inflater, view); @@ -64,6 +68,17 @@ public class VoiceAnnouncesFragment extends BaseSettingsFragment implements OnPr @Override protected void updateToolbar() { super.updateToolbar(); + View view = getView(); + ImageView profileIcon = view.findViewById(R.id.profile_icon); + profileIcon.setImageDrawable(app.getUIUtilities().getIcon(R.drawable.ic_action_help_online, isNightMode() ? R.color.icon_color_default_dark : R.color.icon_color_default_light)); + profileIcon.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (getContext() != null) { + WikipediaDialogFragment.showFullArticle(getContext(), Uri.parse(OSMAND_VOICE_NAVIGATION_URL), isNightMode()); + } + } + }); updateToolbarSwitch(); } @@ -78,7 +93,7 @@ public class VoiceAnnouncesFragment extends BaseSettingsFragment implements OnPr View switchContainer = view.findViewById(R.id.toolbar_switch_container); AndroidUtils.setBackground(switchContainer, new ColorDrawable(color)); - SwitchCompat switchView = (SwitchCompat) switchContainer.findViewById(R.id.switchWidget); + SwitchCompat switchView = switchContainer.findViewById(R.id.switchWidget); switchView.setChecked(checked); UiUtilities.setupCompoundButton(switchView, isNightMode(), TOOLBAR); @@ -102,13 +117,13 @@ public class VoiceAnnouncesFragment extends BaseSettingsFragment implements OnPr enableDisablePreferences(!settings.VOICE_MUTE.getModeValue(getSelectedAppMode())); setupSpeakCamerasPref(); - setupSpeedCamerasAlert(); + setupTurnScreenOnNavigationInstructionsPref(); } private void setupSpeedLimitExceedPref() { //array size must be equal! - Float[] valuesKmh = new Float[] {-10f, -7f, -5f, 0f, 5f, 7f, 10f, 15f, 20f}; - Float[] valuesMph = new Float[] {-7f, -5f, -3f, 0f, 3f, 5f, 7f, 10f, 15f}; + Float[] valuesKmh = new Float[]{-10f, -7f, -5f, 0f, 5f, 7f, 10f, 15f, 20f}; + Float[] valuesMph = new Float[]{-7f, -5f, -3f, 0f, 3f, 5f, 7f, 10f, 15f}; String[] names; if (settings.METRIC_SYSTEM.getModeValue(getSelectedAppMode()) == MetricsConstants.KILOMETERS_AND_METERS) { names = new String[valuesKmh.length]; @@ -121,34 +136,34 @@ public class VoiceAnnouncesFragment extends BaseSettingsFragment implements OnPr names[i] = valuesMph[i].intValue() + " " + getString(R.string.mile_per_hour); } } - ListPreferenceEx voiceProvider = (ListPreferenceEx) findPreference(settings.SPEED_LIMIT_EXCEED_KMH.getId()); + ListPreferenceEx voiceProvider = findPreference(settings.SPEED_LIMIT_EXCEED_KMH.getId()); voiceProvider.setEntries(names); voiceProvider.setEntryValues(valuesKmh); } private void setupKeepInformingPref() { - Integer[] keepInformingValues = new Integer[] {0, 1, 2, 3, 5, 7, 10, 15, 20, 25, 30}; + Integer[] keepInformingValues = new Integer[]{0, 1, 2, 3, 5, 7, 10, 15, 20, 25, 30}; String[] keepInformingNames = new String[keepInformingValues.length]; keepInformingNames[0] = getString(R.string.keep_informing_never); for (int i = 1; i < keepInformingValues.length; i++) { keepInformingNames[i] = keepInformingValues[i] + " " + getString(R.string.int_min); } - ListPreferenceEx keepInforming = (ListPreferenceEx) findPreference(settings.KEEP_INFORMING.getId()); + ListPreferenceEx keepInforming = findPreference(settings.KEEP_INFORMING.getId()); keepInforming.setEntries(keepInformingNames); keepInforming.setEntryValues(keepInformingValues); } private void setupArrivalAnnouncementPref() { - Float[] arrivalValues = new Float[] {1.5f, 1f, 0.5f, 0.25f}; - String[] arrivalNames = new String[] { + Float[] arrivalValues = new Float[]{1.5f, 1f, 0.5f, 0.25f}; + String[] arrivalNames = new String[]{ getString(R.string.arrival_distance_factor_early), getString(R.string.arrival_distance_factor_normally), getString(R.string.arrival_distance_factor_late), getString(R.string.arrival_distance_factor_at_last) }; - ListPreferenceEx arrivalDistanceFactor = (ListPreferenceEx) findPreference(settings.ARRIVAL_DISTANCE_FACTOR.getId()); + ListPreferenceEx arrivalDistanceFactor = findPreference(settings.ARRIVAL_DISTANCE_FACTOR.getId()); arrivalDistanceFactor.setEntries(arrivalNames); arrivalDistanceFactor.setEntryValues(arrivalValues); } @@ -178,21 +193,21 @@ public class VoiceAnnouncesFragment extends BaseSettingsFragment implements OnPr Drawable enabled = getActiveIcon(R.drawable.ic_action_volume_up); Drawable icon = getPersistentPrefIcon(enabled, disabled); - ListPreferenceEx voiceProvider = (ListPreferenceEx) findPreference(settings.VOICE_PROVIDER.getId()); + ListPreferenceEx voiceProvider = findPreference(settings.VOICE_PROVIDER.getId()); voiceProvider.setEntries(entries); voiceProvider.setEntryValues(entryValues); voiceProvider.setIcon(icon); } private void setupAudioStreamGuidancePref() { - String[] streamTypes = new String[] { + String[] streamTypes = new String[]{ getString(R.string.voice_stream_music), getString(R.string.voice_stream_notification), getString(R.string.voice_stream_voice_call) }; //getString(R.string.shared_string_default)}; - Integer[] streamIntTypes = new Integer[] { + Integer[] streamIntTypes = new Integer[]{ AudioManager.STREAM_MUSIC, AudioManager.STREAM_NOTIFICATION, AudioManager.STREAM_VOICE_CALL @@ -208,6 +223,11 @@ public class VoiceAnnouncesFragment extends BaseSettingsFragment implements OnPr getPreferenceScreen().addPreference(interruptMusicPref); } + private void setupTurnScreenOnNavigationInstructionsPref() { + SwitchPreferenceEx turnScreenOnNavigationInstructions = findPreference(settings.TURN_SCREEN_ON_NAVIGATION_INSTRUCTIONS.getId()); + turnScreenOnNavigationInstructions.setDescription(R.string.turn_screen_on_navigation_instructions_descr); + } + private void updateMenu() { MapActivity mapActivity = getMapActivity(); if (mapActivity != null) { @@ -319,12 +339,11 @@ public class VoiceAnnouncesFragment extends BaseSettingsFragment implements OnPr public void onPreferenceChanged(String prefId) { if (prefId.equals(settings.SPEED_CAMERAS_UNINSTALLED.getId())) { setupSpeakCamerasPref(); - setupSpeedCamerasAlert(); } } private void setupSpeakCamerasPref() { - SwitchPreferenceCompat showCameras = (SwitchPreferenceCompat) findPreference(settings.SPEAK_SPEED_CAMERA.getId()); + SwitchPreferenceCompat showCameras = findPreference(settings.SPEAK_SPEED_CAMERA.getId()); showCameras.setVisible(!settings.SPEED_CAMERAS_UNINSTALLED.get()); } } \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/srtmplugin/TerrainFragment.java b/OsmAnd/src/net/osmand/plus/srtmplugin/TerrainFragment.java index 1475a0a7fd..ae15dfbf6e 100644 --- a/OsmAnd/src/net/osmand/plus/srtmplugin/TerrainFragment.java +++ b/OsmAnd/src/net/osmand/plus/srtmplugin/TerrainFragment.java @@ -21,7 +21,6 @@ import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.widget.SwitchCompat; -import androidx.core.content.ContextCompat; import com.github.ksoichiro.android.observablescrollview.ObservableListView; import com.google.android.material.slider.RangeSlider; @@ -50,14 +49,16 @@ import org.apache.commons.logging.Log; import java.io.IOException; import java.lang.ref.WeakReference; import java.util.List; +import java.util.Locale; -import static net.osmand.plus.UiUtilities.CustomRadioButtonType.*; +import static net.osmand.plus.UiUtilities.CustomRadioButtonType.END; +import static net.osmand.plus.UiUtilities.CustomRadioButtonType.START; import static net.osmand.plus.download.DownloadActivityType.HILLSHADE_FILE; import static net.osmand.plus.download.DownloadActivityType.SLOPE_FILE; -import static net.osmand.plus.srtmplugin.TerrainMode.HILLSHADE; -import static net.osmand.plus.srtmplugin.TerrainMode.SLOPE; import static net.osmand.plus.srtmplugin.SRTMPlugin.TERRAIN_MAX_ZOOM; import static net.osmand.plus.srtmplugin.SRTMPlugin.TERRAIN_MIN_ZOOM; +import static net.osmand.plus.srtmplugin.TerrainMode.HILLSHADE; +import static net.osmand.plus.srtmplugin.TerrainMode.SLOPE; public class TerrainFragment extends BaseOsmAndFragment implements View.OnClickListener, @@ -102,7 +103,7 @@ public class TerrainFragment extends BaseOsmAndFragment implements View.OnClickL private ArrayAdapter listAdapter; - private Slider.OnChangeListener transparencySliderChangeListener = new Slider.OnChangeListener() { + private final Slider.OnChangeListener transparencySliderChangeListener = new Slider.OnChangeListener() { @Override public void onValueChange(@NonNull Slider slider, float value, boolean fromUser) { if (fromUser) { @@ -114,7 +115,7 @@ public class TerrainFragment extends BaseOsmAndFragment implements View.OnClickL } }; - private RangeSlider.OnChangeListener zoomSliderChangeListener = new RangeSlider.OnChangeListener() { + private final RangeSlider.OnChangeListener zoomSliderChangeListener = new RangeSlider.OnChangeListener() { @Override public void onValueChange(@NonNull RangeSlider slider, float value, boolean fromUser) { List values = slider.getValues(); @@ -186,11 +187,7 @@ public class TerrainFragment extends BaseOsmAndFragment implements View.OnClickL getString(R.string.slope_read_more), wikiString ); - String emptyStateText = String.format( - getString(R.string.ltr_or_rtl_combine_via_space), - getString(R.string.terrain_empty_state_text), - PLUGIN_URL - ); + String emptyStateText = getString(R.string.terrain_empty_state_text) + "\n" + PLUGIN_URL; setupClickableText(slopeReadMoreTv, readMoreText, wikiString, SLOPES_WIKI_URL, false); setupClickableText(emptyStateDescriptionTv, emptyStateText, PLUGIN_URL, PLUGIN_URL, true); @@ -290,10 +287,10 @@ public class TerrainFragment extends BaseOsmAndFragment implements View.OnClickL } private void setupClickableText(TextView textView, - String text, - String clickableText, - final String url, - final boolean medium) { + String text, + String clickableText, + final String url, + final boolean medium) { SpannableString spannableString = new SpannableString(text); ClickableSpan clickableSpan = new ClickableSpan() { @Override diff --git a/OsmAnd/src/net/osmand/plus/track/GradientCard.java b/OsmAnd/src/net/osmand/plus/track/GradientCard.java index 217ad2a8f1..3c34d9f68d 100644 --- a/OsmAnd/src/net/osmand/plus/track/GradientCard.java +++ b/OsmAnd/src/net/osmand/plus/track/GradientCard.java @@ -12,6 +12,7 @@ import net.osmand.plus.R; import net.osmand.plus.activities.MapActivity; import net.osmand.plus.helpers.AndroidUiHelper; import net.osmand.plus.routepreparationmenu.cards.BaseCard; +import net.osmand.router.RouteColorize; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -42,8 +43,9 @@ public class GradientCard extends BaseCard { AndroidUiHelper.updateVisibility(view, true); TextView minValue = view.findViewById(R.id.min_value); TextView maxValue = view.findViewById(R.id.max_value); - float min = getMinValue(); - float max = getMaxValue(min); + double min = RouteColorize.getMinValue(selectedScaleType.toColorizationType(), gpxTrackAnalysis); + double max = RouteColorize.getMaxValue(selectedScaleType.toColorizationType(), + gpxTrackAnalysis, min, app.getSettings().getApplicationMode().getMaxSpeed()); minValue.setText(formatValue(min)); maxValue.setText(formatValue(max)); } @@ -53,27 +55,13 @@ public class GradientCard extends BaseCard { updateContent(); } - private float getMinValue() { - return (float) (selectedScaleType == GradientScaleType.ALTITUDE ? gpxTrackAnalysis.minElevation : 0.0); - } - - private float getMaxValue(float minValue) { - if (selectedScaleType == GradientScaleType.SPEED) { - return (Math.max(gpxTrackAnalysis.maxSpeed, app.getSettings().getApplicationMode().getMaxSpeed())); - } else if (selectedScaleType == GradientScaleType.ALTITUDE) { - return (float) Math.max(gpxTrackAnalysis.maxElevation, minValue + 50); - } else { - return 25; - } - } - - private CharSequence formatValue(float value) { + private CharSequence formatValue(double value) { if (selectedScaleType == GradientScaleType.ALTITUDE) { return OsmAndFormatter.getFormattedAlt(value, app); } else if (selectedScaleType == GradientScaleType.SLOPE) { return (int) value + " %"; } - String speed = OsmAndFormatter.getFormattedSpeed(value, app); + String speed = OsmAndFormatter.getFormattedSpeed((float) value, app); String speedUnit = app.getSettings().SPEED_SYSTEM.get().toShortString(app); Spannable formattedSpeed = new SpannableString(speed); formattedSpeed.setSpan( diff --git a/OsmAnd/src/net/osmand/plus/views/Renderable.java b/OsmAnd/src/net/osmand/plus/views/Renderable.java index 27d741d964..657cf3856a 100644 --- a/OsmAnd/src/net/osmand/plus/views/Renderable.java +++ b/OsmAnd/src/net/osmand/plus/views/Renderable.java @@ -61,6 +61,7 @@ public class Renderable { public List points = null; // Original list of points protected List culled = new ArrayList<>(); // Reduced/resampled list of points + protected List oldCulled = new ArrayList<>(); protected int pointSize; protected double segmentSize; @@ -116,7 +117,7 @@ public class Renderable { updateLocalPaint(p); canvas.rotate(-tileBox.getRotate(), tileBox.getCenterPixelX(), tileBox.getCenterPixelY()); if (scaleType != null) { - drawGradient(getPointsForDrawing(), p, canvas, tileBox); + drawGradient(getPointsForDrawingWithBorder(), p, canvas, tileBox); } else { drawSolid(getPointsForDrawing(), p, canvas, tileBox); } @@ -126,6 +127,9 @@ public class Renderable { public void drawSegment(double zoom, Paint p, Canvas canvas, RotatedTileBox tileBox) { if (QuadRect.trivialOverlap(tileBox.getLatLonBounds(), trackBounds)) { // is visible? + if (tileBox.getZoomAnimation() > 0 && !Algorithms.isEmpty(culled) && scaleType != null) { + oldCulled = new ArrayList<>(culled); + } startCuller(zoom); drawSingleSegment(zoom, p, canvas, tileBox); } @@ -139,6 +143,16 @@ public class Renderable { return culled.isEmpty() ? points : culled; } + public List getPointsForDrawingWithBorder() { + if (!culled.isEmpty()) { + return culled; + } else if (!oldCulled.isEmpty()) { + return oldCulled; + } else { + return points; + } + } + public void drawGeometry(Canvas canvas, RotatedTileBox tileBox, QuadRect quadRect, int arrowColor, int trackColor, float trackWidth) { if (geometryWay != null) { List points = getPointsForDrawing(); @@ -290,7 +304,8 @@ public class Renderable { super(pt, 0); } - @Override public void drawSegment(double zoom, Paint p, Canvas canvas, RotatedTileBox tileBox) { + @Override + public void drawSegment(double zoom, Paint p, Canvas canvas, RotatedTileBox tileBox) { if (points.size() != pointSize) { int prevSize = pointSize; pointSize = points.size(); diff --git a/OsmAnd/src/net/osmand/plus/views/layers/GPXLayer.java b/OsmAnd/src/net/osmand/plus/views/layers/GPXLayer.java index c16d687614..69266746a9 100644 --- a/OsmAnd/src/net/osmand/plus/views/layers/GPXLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/layers/GPXLayer.java @@ -680,15 +680,19 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM private void drawSelectedFileSegments(SelectedGpxFile selectedGpxFile, boolean currentTrack, Canvas canvas, RotatedTileBox tileBox, DrawSettings settings) { + OsmandApplication app = view.getApplication(); GPXFile gpxFile = selectedGpxFile.getGpxFile(); List segments = selectedGpxFile.getPointsToDisplay(); GradientScaleType scaleType = getGradientScaleType(gpxFile); List colorsOfPoints = null; - if (scaleType != null) { - RouteColorize colorize = new RouteColorize(view.getZoom(), gpxFile, scaleType.toColorizationType()); + + if (needCalculatePointsColors(segments, scaleType)) { + RouteColorize colorize = new RouteColorize(view.getZoom(), gpxFile, selectedGpxFile.getTrackAnalysis(app), + scaleType.toColorizationType(), app.getSettings().getApplicationMode().getMaxSpeed()); colorize.setPalette(getColorizationPalette(gpxFile, scaleType)); colorsOfPoints = colorize.getResult(false); } + int startIdx = 0; for (TrkSegment ts : segments) { String width = getTrackWidthName(gpxFile, defaultTrackWidthPref.get()); @@ -716,6 +720,25 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM } } + private boolean needCalculatePointsColors(List segments, GradientScaleType scaleType) { + if (scaleType == null) { + return false; + } + RouteColorize.ColorizationType colorizationType = scaleType.toColorizationType(); + for (int segIdx = segments.size() - 1; segIdx >= 0; segIdx--) { + List pts = segments.get(segIdx).points; + if (!Algorithms.isEmpty(pts)) { + for (int wptIdx = pts.size() - 1; wptIdx >= 0; wptIdx--) { + WptPt pt = pts.get(wptIdx); + if (pt.getColor(colorizationType) == 0) { + return true; + } + } + } + } + return false; + } + private int setColorsToPoints(TrkSegment segment, List colors, GradientScaleType scaleType, int startIdx) { int pointsSize = segment.points.size(); RouteColorize.RouteColorizationPoint startColor = colors.get(startIdx); diff --git a/OsmAnd/src/net/osmand/plus/views/layers/MapControlsLayer.java b/OsmAnd/src/net/osmand/plus/views/layers/MapControlsLayer.java index 22c077f02d..a4158feced 100644 --- a/OsmAnd/src/net/osmand/plus/views/layers/MapControlsLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/layers/MapControlsLayer.java @@ -97,8 +97,6 @@ public class MapControlsLayer extends OsmandMapLayer { private static final int REQUEST_LOCATION_FOR_NAVIGATION_FAB_PERMISSION = 201; private static final int REQUEST_LOCATION_FOR_ADD_DESTINATION_PERMISSION = 202; - private static final int COMPASS_PRESSED_TIME_INTERVAL_MS = 5000; - public MapHudButton createHudButton(View iv, int resId, String id) { MapHudButton mc = new MapHudButton(); mc.iv = iv; @@ -139,7 +137,6 @@ public class MapControlsLayer extends OsmandMapLayer { private MapQuickActionLayer mapQuickActionLayer; private boolean forceShowCompass; private LatLon requestedLatLon; - private long compassPressed; private Set themeInfoProviderTags = new HashSet<>(); public MapControlsLayer(MapActivity activity) { @@ -292,20 +289,7 @@ public class MapControlsLayer extends OsmandMapLayer { compass.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - boolean followingMode = app.getRoutingHelper().isFollowingMode(); - - if (followingMode) { - if (compassPressed + COMPASS_PRESSED_TIME_INTERVAL_MS > System.currentTimeMillis()) { - compassPressed = 0; - mapActivity.getMapViewTrackingUtilities().switchRotateMapMode(); - } else { - compassPressed = System.currentTimeMillis(); - app.showShortToastMessage(app.getString(R.string.press_again_to_change_the_map_orientation)); - } - } else { - compassPressed = 0; - mapActivity.getMapViewTrackingUtilities().switchRotateMapMode(); - } + mapActivity.getMapViewTrackingUtilities().switchRotateMapMode(); } }); diff --git a/OsmAnd/src/net/osmand/plus/views/layers/geometry/GeometryWay.java b/OsmAnd/src/net/osmand/plus/views/layers/geometry/GeometryWay.java index 9c7fd1c782..ab98990d36 100644 --- a/OsmAnd/src/net/osmand/plus/views/layers/geometry/GeometryWay.java +++ b/OsmAnd/src/net/osmand/plus/views/layers/geometry/GeometryWay.java @@ -11,6 +11,8 @@ import net.osmand.Location; import net.osmand.data.RotatedTileBox; import net.osmand.util.MapAlgorithms; import net.osmand.util.MapUtils; +import net.osmand.plus.views.layers.geometry.MultiProfileGeometryWay.GeometryMultiProfileWayStyle; + import java.util.ArrayList; import java.util.Collections; @@ -174,8 +176,8 @@ public abstract class GeometryWay style, List tx, List ty, List angles, List distances, double dist, List> styles) { @@ -333,7 +342,7 @@ public abstract class GeometryWay tx, List ty, + protected void drawRouteSegment(RotatedTileBox tb, Canvas canvas, List tx, List ty, List angles, List distances, double distToFinish, List> styles) { if (tx.size() < 2) { return; @@ -449,4 +458,4 @@ public abstract class GeometryWay { + + private static final String DEFAULT_PROFILE_KEY = ApplicationMode.DEFAULT.getStringKey(); + + private Map, RoadSegmentData> segmentData; + private List beforeSegments; + private List afterSegments; + + public MultiProfileGeometryWay(MultiProfileGeometryWayContext context) { + super(context, new MultiProfileGeometryWayDrawer(context)); + } + + public void drawSegments(Canvas canvas, RotatedTileBox tileBox) { + QuadRect bounds = tileBox.getLatLonBounds(); + drawSegments(tileBox, canvas, bounds.top, bounds.left, bounds.bottom, bounds.right, null, 0); + } + + public void updateRoute(RotatedTileBox tileBox, Map, RoadSegmentData> segmentData, + List beforeSegments, List afterSegments) { + boolean shouldUpdateRoute = tileBox.getMapDensity() != getMapDensity() || segmentDataChanged(segmentData) + || this.beforeSegments != beforeSegments || this.afterSegments != afterSegments || getLocationProvider() == null; + if (shouldUpdateRoute) { + this.segmentData = segmentData; + this.beforeSegments = beforeSegments; + this.afterSegments = afterSegments; + + List locations; + Map> styleMap; + List ways = new ArrayList<>(); + List> styles = new ArrayList<>(); + locations = new ArrayList<>(); + + List allSegments = new ArrayList<>(); + allSegments.addAll(beforeSegments); + allSegments.addAll(afterSegments); + setStyles(allSegments, ways, styles); + + styleMap = new TreeMap<>(); + int i = 0; + int k = 0; + if (ways.size() > 0) { + for (Way w : ways) { + styleMap.put(k, styles.get(i++)); + for (Node n : w.getNodes()) { + Location ln = new Location(""); + ln.setLatitude(n.getLatitude()); + ln.setLongitude(n.getLongitude()); + locations.add(ln); + k++; + } + } + } + + updateWay(locations, styleMap, tileBox); + } + } + + @Override + public void clearWay() { + super.clearWay(); + if (segmentData != null) { + segmentData.clear(); + } + } + + private void setStyles(List segments, List ways, List> styles) { + for (TrkSegment segment : segments) { + List points = segment.points; + for (int i = 0; i < points.size() - 1; i++) { + setStylesInternal(points, i, ways, styles); + } + styles.add(new GeometryMultiProfileWayStyle(getContext(), new ArrayList(), 0, 0, true)); + Way way = new Way(-1); + WptPt last = points.get(points.size() - 1); + way.addNode(new Node(last.lat, last.lon, -1)); + ways.add(way); + } + } + + private void setStylesInternal(List points, int idx, List ways, List> styles) { + WptPt startPt = points.get(idx); + WptPt endPt = points.get(idx + 1); + List routePoints = getRoutePoints(startPt, endPt); + boolean isSecondToLast = idx + 2 == points.size(); + + Way way = new Way(-1); + String currProfileKey = getProfileKey(startPt); + Pair profileData = getProfileData(currProfileKey); + GeometryMultiProfileWayStyle style = new GeometryMultiProfileWayStyle( + getContext(), routePoints, profileData.first, profileData.second); + styles.add(style); + ways.add(way); + + for (LatLon routePt : routePoints) { + if (isSecondToLast || routePt.getLatitude() != endPt.getLatitude() + && routePt.getLongitude() != endPt.getLongitude()) { + way.addNode(new Node(routePt.getLatitude(), routePt.getLongitude(), -1)); + } + } + } + + private List getRoutePoints(WptPt start, WptPt end) { + Pair userLine = new Pair<>(start, end); + RoadSegmentData roadSegmentData = segmentData.get(userLine); + List routePoints = new ArrayList<>(); + + if (roadSegmentData == null || Algorithms.isEmpty(roadSegmentData.getPoints())) { + routePoints.add(new LatLon(start.lat, start.lon)); + routePoints.add(new LatLon(end.lat, end.lon)); + } else { + for (WptPt routePt : roadSegmentData.getPoints()) { + routePoints.add(new LatLon(routePt.lat, routePt.lon)); + } + } + return routePoints; + } + + @Override + protected boolean shouldAddLocation(RotatedTileBox tileBox, double leftLon, double rightLon, + double bottomLat, double topLat, GeometryWayProvider provider, + int currLocationIdx) { + float currX = tileBox.getPixXFromLatLon(provider.getLatitude(currLocationIdx), provider.getLongitude(currLocationIdx)); + float currY = tileBox.getPixYFromLatLon(provider.getLatitude(currLocationIdx), provider.getLongitude(currLocationIdx)); + if (tileBox.containsPoint(currX, currY, getContext().circleSize)) { + return true; + } else if (currLocationIdx + 1 >= provider.getSize()) { + return false; + } + float nextX = tileBox.getPixXFromLatLon(provider.getLatitude(currLocationIdx + 1), provider.getLongitude(currLocationIdx + 1)); + float nextY = tileBox.getPixXFromLatLon(provider.getLatitude(currLocationIdx + 1), provider.getLongitude(currLocationIdx + 1)); + return tileBox.containsPoint(nextX, nextY, getContext().circleSize); + } + + private boolean segmentDataChanged(Map, RoadSegmentData> other) { + if (other.size() != segmentData.size()) { + return true; + } + for (Pair data : other.keySet()) { + if (other.get(data) != segmentData.get(data)) { + return true; + } + } + return false; + } + + @NonNull + private String getProfileKey(WptPt pt) { + String key = pt.getProfileType(); + return key == null ? DEFAULT_PROFILE_KEY : key; + } + + private Pair getProfileData(String profileKey) { + boolean night = getContext().isNightMode(); + ApplicationMode mode = ApplicationMode.valueOfStringKey(profileKey, ApplicationMode.DEFAULT); + return ApplicationMode.DEFAULT.getStringKey().equals(mode.getStringKey()) ? + new Pair<>(ContextCompat.getColor(getContext().getCtx(), ProfileIconColors.DARK_YELLOW.getColor(night)), R.drawable.ic_action_split_interval) : + new Pair<>(mode.getProfileColor(night), mode.getIconRes()); + } + + @NonNull + @Override + public GeometryWayStyle getDefaultWayStyle() { + return null; + } + + public static class GeometryMultiProfileWayStyle extends GeometryWayStyle { + + @ColorInt + private final int lineColor; + @ColorInt + private final int borderColor; + @DrawableRes + private final int profileIconRes; + + private final boolean isGap; + + private final List routePoints; + + public GeometryMultiProfileWayStyle(MultiProfileGeometryWayContext context, List routePoints, + @ColorInt int profileColor, @DrawableRes int profileIconRes, + boolean isGap) { + super(context); + this.routePoints = routePoints; + this.lineColor = profileColor; + this.borderColor = ColorUtils.blendARGB(profileColor, Color.BLACK, 0.2f); + this.profileIconRes = profileIconRes; + this.isGap = isGap; + } + + + public GeometryMultiProfileWayStyle(MultiProfileGeometryWayContext context, List routePoints, + @ColorInt int profileColor, @DrawableRes int profileIconRes) { + this(context, routePoints, profileColor, profileIconRes, false); + } + + @ColorInt + public int getBorderColor() { + return borderColor; + } + + @ColorInt + public int getLineColor() { + return lineColor; + } + + @Override + public Bitmap getPointBitmap() { + return getContext().getProfileIconBitmap(profileIconRes, borderColor); + } + + public List getRoutePoints() { + return routePoints; + } + + public boolean isGap() { + return isGap; + } + + @Override + public boolean equals(Object other) { + return this == other; + } + + @Override + public boolean hasPathLine() { + return true; + } + } +} \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/views/layers/geometry/MultiProfileGeometryWayContext.java b/OsmAnd/src/net/osmand/plus/views/layers/geometry/MultiProfileGeometryWayContext.java new file mode 100644 index 0000000000..d6ef3cfa72 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/views/layers/geometry/MultiProfileGeometryWayContext.java @@ -0,0 +1,106 @@ +package net.osmand.plus.views.layers.geometry; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; + +import net.osmand.AndroidUtils; +import net.osmand.plus.R; +import net.osmand.plus.UiUtilities; +import net.osmand.plus.views.OsmandMapLayer.RenderingLineAttributes; +import net.osmand.util.Algorithms; + +import java.util.HashMap; +import java.util.Map; + +import androidx.annotation.ColorInt; +import androidx.annotation.DrawableRes; +import androidx.annotation.NonNull; + +public class MultiProfileGeometryWayContext extends GeometryWayContext { + + private final UiUtilities iconsCache; + + public final float minIconMargin; + public final float circleSize; + public final float pointIconSize; + + private static final String pointColorHex = "#637EFB"; + + private RenderingLineAttributes multiProfileAttrs; + + private Bitmap pointIcon; + private final Map profileIconsBitmapCache; + + public MultiProfileGeometryWayContext(Context ctx, UiUtilities iconsCache, float density) { + super(ctx, density); + this.iconsCache = iconsCache; + profileIconsBitmapCache = new HashMap<>(); + minIconMargin = density * 30; + circleSize = density * 70; + pointIconSize = density * 22f; + } + + public void updatePaints(boolean nightMode, @NonNull RenderingLineAttributes multiProfileAttrs) { + this.multiProfileAttrs = multiProfileAttrs; + super.updatePaints(nightMode, multiProfileAttrs); + } + + @Override + protected void recreateBitmaps() { + float density = getDensity(); + float outerRadius = density * 11f; + float centerRadius = density * 10.5f; + float innerRadius = density * 6.5f; + float centerXY = pointIconSize / 2; + + pointIcon = Bitmap.createBitmap((int) pointIconSize, (int) pointIconSize, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(pointIcon); + Paint paint = new Paint(); + paint.setStyle(Paint.Style.FILL); + + paint.setColor(Color.BLACK); + canvas.drawCircle(centerXY, centerXY, outerRadius, paint); + + paint.setColor(Color.WHITE); + canvas.drawCircle(centerXY, centerXY, centerRadius, paint); + + paint.setColor(Algorithms.parseColor(pointColorHex)); + canvas.drawCircle(centerXY, centerXY, innerRadius, paint); + } + + @NonNull + public Bitmap getProfileIconBitmap(@DrawableRes int iconRes, @ColorInt int color) { + String key = iconRes + "_" + color; + Bitmap bitmap = profileIconsBitmapCache.get(key); + if (bitmap == null) { + bitmap = Bitmap.createBitmap((int) circleSize, (int) circleSize, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(bitmap); + float center = bitmap.getWidth() / 2f; + + canvas.drawCircle(center, center, center / 2, multiProfileAttrs.paint_1); + multiProfileAttrs.paint3.setColor(color); + canvas.drawCircle(center, center, center / 2, multiProfileAttrs.paint3); + + float iconSize = center - getDensity() * 10; + Bitmap profileIconBitmap = AndroidUtils.createScaledBitmap( + iconsCache.getPaintedIcon(iconRes, color), (int) iconSize, (int) iconSize); + canvas.drawBitmap(profileIconBitmap, center - iconSize / 2, center - iconSize / 2, multiProfileAttrs.paint3); + + profileIconsBitmapCache.put(key, bitmap); + } + return bitmap; + } + + @NonNull + public Bitmap getPointIcon() { + return pointIcon; + } + + @Override + protected int getArrowBitmapResId() { + return R.drawable.ic_action_split_interval; + } +} \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/views/layers/geometry/MultiProfileGeometryWayDrawer.java b/OsmAnd/src/net/osmand/plus/views/layers/geometry/MultiProfileGeometryWayDrawer.java new file mode 100644 index 0000000000..c9e5f8101f --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/views/layers/geometry/MultiProfileGeometryWayDrawer.java @@ -0,0 +1,84 @@ +package net.osmand.plus.views.layers.geometry; + +import android.graphics.Canvas; +import android.graphics.Path; +import android.graphics.PathMeasure; +import android.graphics.PointF; + +import net.osmand.data.LatLon; +import net.osmand.data.RotatedTileBox; +import net.osmand.plus.views.OsmandMapLayer.RenderingLineAttributes; +import net.osmand.plus.views.layers.geometry.MultiProfileGeometryWay.GeometryMultiProfileWayStyle; +import net.osmand.util.Algorithms; + +import java.util.List; + +public class MultiProfileGeometryWayDrawer extends GeometryWayDrawer { + + public MultiProfileGeometryWayDrawer(MultiProfileGeometryWayContext context) { + super(context); + } + + @Override + public void drawPath(Canvas canvas, Path path, GeometryWayStyle style) { + if (style instanceof GeometryMultiProfileWayStyle && !((GeometryMultiProfileWayStyle) style).isGap()) { + RenderingLineAttributes attrs = getContext().getAttrs(); + + attrs.paint.setColor(((GeometryMultiProfileWayStyle) style).getBorderColor()); + canvas.drawPath(path, attrs.paint); + + attrs.paint2.setColor(((GeometryMultiProfileWayStyle) style).getLineColor()); + canvas.drawPath(path, attrs.paint2); + } + } + + @Override + public void drawArrowsOverPath(Canvas canvas, RotatedTileBox tb, List tx, List ty, List angles, List distances, double distPixToFinish, List> styles) { + Path path = new Path(); + PathMeasure pathMeasure = new PathMeasure(); + MultiProfileGeometryWayContext context = getContext(); + GeometryMultiProfileWayStyle style = null; + + for (int i = 0; i < styles.size(); i++) { + GeometryWayStyle s = styles.get(i); + if (s != null && !s.equals(style) || !((GeometryMultiProfileWayStyle) s).isGap()) { + style = (GeometryMultiProfileWayStyle) styles.get(i); + PointF center = getIconCenter(tb, style.getRoutePoints(), path, pathMeasure); + if (center != null && tb.containsPoint(center.x, center.y, context.circleSize)) { + float x = center.x - context.circleSize / 2; + float y = center.y - context.circleSize / 2; + canvas.drawBitmap(style.getPointBitmap(), x, y, null); + } + } + } + } + + private PointF getIconCenter(RotatedTileBox tileBox, List routePoints, Path path, PathMeasure pathMeasure) { + if (Algorithms.isEmpty(routePoints)) { + return null; + } + + path.reset(); + PointF first = getPoint(tileBox, routePoints.get(0)); + path.moveTo(first.x, first.y); + for (int i = 1; i < routePoints.size(); i++) { + PointF pt = getPoint(tileBox, routePoints.get(i)); + path.lineTo(pt.x, pt.y); + } + + pathMeasure.setPath(path, false); + float routeLength = pathMeasure.getLength(); + if ((routeLength - getContext().circleSize) / 2 < getContext().minIconMargin) { + return null; + } + + float[] xy = new float[2]; + pathMeasure.getPosTan(routeLength * 0.5f, xy, null); + return new PointF(xy[0], xy[1]); + } + + private PointF getPoint(RotatedTileBox tileBox, LatLon latLon) { + return new PointF(tileBox.getPixXFromLatLon(latLon.getLatitude(), latLon.getLongitude()), + tileBox.getPixYFromLatLon(latLon.getLatitude(), latLon.getLongitude())); + } +} \ No newline at end of file