From cc4af37204824f7ca283833afa8400d4c98411b1 Mon Sep 17 00:00:00 2001 From: nazar-kutz Date: Mon, 26 Oct 2020 14:03:49 +0200 Subject: [PATCH 1/8] graph iteration 2 strings --- OsmAnd/res/values/strings.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/OsmAnd/res/values/strings.xml b/OsmAnd/res/values/strings.xml index a30f9c4fc5..be2d6126ed 100644 --- a/OsmAnd/res/values/strings.xml +++ b/OsmAnd/res/values/strings.xml @@ -11,6 +11,7 @@ Thx - Hardy --> + You must add at least two points. Wait for the route recalculation.\nGraph will be available after recalculation. %1$s data available only on the roads, you need to calculate a route using “Route between points” to get it. Graph From e4fe6564e6f057d330527f3588a02fb4fa373faf Mon Sep 17 00:00:00 2001 From: nazar-kutz Date: Fri, 30 Oct 2020 19:41:12 +0200 Subject: [PATCH 2/8] Fix bugs, complete graphs logic, remove copy-paste, refactoring --- .../fragment_measurement_tool_graph.xml | 65 ++- OsmAnd/res/values/attrs.xml | 1 + OsmAnd/res/values/styles.xml | 2 + .../plus/helpers/CustomBarChartRenderer.java | 30 ++ .../net/osmand/plus/helpers/GpxUiHelper.java | 67 ++++ .../plus/measurementtool/GraphsCard.java | 371 +++++++++--------- .../MeasurementEditingContext.java | 4 + .../MeasurementToolFragment.java | 62 ++- .../graph/BaseGraphAdapter.java | 82 ++++ .../graph/CommonGraphAdapter.java | 152 +++++++ .../graph/CustomGraphAdapter.java | 187 +++++++++ .../graph/GraphAdapterHelper.java | 145 +++++++ .../plus/myplaces/TrackSegmentFragment.java | 69 +--- .../RouteDetailsFragment.java | 172 ++------ .../routepreparationmenu/cards/BaseCard.java | 9 - .../cards/RouteInfoCard.java | 142 ++----- .../cards/RouteStatisticCard.java | 131 +------ 17 files changed, 1036 insertions(+), 655 deletions(-) create mode 100644 OsmAnd/src/net/osmand/plus/helpers/CustomBarChartRenderer.java create mode 100644 OsmAnd/src/net/osmand/plus/measurementtool/graph/BaseGraphAdapter.java create mode 100644 OsmAnd/src/net/osmand/plus/measurementtool/graph/CommonGraphAdapter.java create mode 100644 OsmAnd/src/net/osmand/plus/measurementtool/graph/CustomGraphAdapter.java create mode 100644 OsmAnd/src/net/osmand/plus/measurementtool/graph/GraphAdapterHelper.java diff --git a/OsmAnd/res/layout/fragment_measurement_tool_graph.xml b/OsmAnd/res/layout/fragment_measurement_tool_graph.xml index 3d14187712..45f0b484d5 100644 --- a/OsmAnd/res/layout/fragment_measurement_tool_graph.xml +++ b/OsmAnd/res/layout/fragment_measurement_tool_graph.xml @@ -51,8 +51,13 @@ android:layout_width="match_parent" android:layout_height="@dimen/route_info_chart_height" /> - + + - + android:orientation="vertical"> + + + + + + + + + + + + diff --git a/OsmAnd/res/values/attrs.xml b/OsmAnd/res/values/attrs.xml index 5ffd847b3a..8e8c8aee32 100644 --- a/OsmAnd/res/values/attrs.xml +++ b/OsmAnd/res/values/attrs.xml @@ -19,6 +19,7 @@ + diff --git a/OsmAnd/res/values/styles.xml b/OsmAnd/res/values/styles.xml index ea305b19d2..d96c5fa196 100644 --- a/OsmAnd/res/values/styles.xml +++ b/OsmAnd/res/values/styles.xml @@ -92,6 +92,7 @@ @style/Widget.Styled.ActionBarLight @color/list_background_color_light @drawable/circle_background_light + @drawable/circle_contour_bg_light @drawable/btn_round_light @drawable/btn_round_border_light @drawable/btn_round_border_light_2 @@ -394,6 +395,7 @@ @style/Widget.Styled.ActionBarDark @color/list_background_color_dark @drawable/circle_background_dark + @drawable/circle_contour_bg_dark @drawable/btn_round_dark @drawable/btn_round_border_dark @drawable/btn_round_border_dark_2 diff --git a/OsmAnd/src/net/osmand/plus/helpers/CustomBarChartRenderer.java b/OsmAnd/src/net/osmand/plus/helpers/CustomBarChartRenderer.java new file mode 100644 index 0000000000..3321399dbf --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/helpers/CustomBarChartRenderer.java @@ -0,0 +1,30 @@ +package net.osmand.plus.helpers; + +import android.graphics.RectF; + +import androidx.annotation.NonNull; + +import com.github.mikephil.charting.charts.BarChart; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.renderer.HorizontalBarChartRenderer; + +import net.osmand.AndroidUtils; + +public class CustomBarChartRenderer extends HorizontalBarChartRenderer { + private float highlightHalfWidth; + + public CustomBarChartRenderer(@NonNull BarChart chart) { + this(chart, AndroidUtils.dpToPx(chart.getContext(), 1f) / 2f); + } + + public CustomBarChartRenderer(@NonNull BarChart chart, float highlightHalfWidth) { + super(chart, chart.getAnimator(), chart.getViewPortHandler()); + this.highlightHalfWidth = highlightHalfWidth; + } + + @Override + protected void setHighlightDrawPos(Highlight high, RectF bar) { + bar.left = high.getDrawX() - highlightHalfWidth; + bar.right = high.getDrawX() + highlightHalfWidth; + } +} diff --git a/OsmAnd/src/net/osmand/plus/helpers/GpxUiHelper.java b/OsmAnd/src/net/osmand/plus/helpers/GpxUiHelper.java index 47650dc2ec..c563be0769 100644 --- a/OsmAnd/src/net/osmand/plus/helpers/GpxUiHelper.java +++ b/OsmAnd/src/net/osmand/plus/helpers/GpxUiHelper.java @@ -75,6 +75,8 @@ import net.osmand.plus.ContextMenuAdapter; import net.osmand.plus.ContextMenuItem; import net.osmand.plus.GPXDatabase.GpxDataItem; import net.osmand.plus.GpxDbHelper.GpxDataItemCallback; +import net.osmand.plus.GpxSelectionHelper.GpxDisplayItem; +import net.osmand.plus.GpxSelectionHelper.GpxDisplayGroup; import net.osmand.plus.GpxSelectionHelper.SelectedGpxFile; import net.osmand.plus.OsmAndConstants; import net.osmand.plus.OsmAndFormatter; @@ -2045,6 +2047,71 @@ public class GpxUiHelper { return gpx; } + public enum LineGraphType { + ALTITUDE, + SLOPE, + SPEED; + } + + public static List getDataSets(LineChart chart, + OsmandApplication app, + GPXTrackAnalysis analysis, + boolean calcWithoutGaps, + LineGraphType... types) { + if (app == null || chart == null || analysis == null || types == null) { + return Collections.emptyList(); + } + List dataList = new ArrayList<>(); + for (LineGraphType type : types) { + switch (type) { + case ALTITUDE: { + if (analysis.hasElevationData) { + OrderedLineDataSet elevationDataSet = GpxUiHelper.createGPXElevationDataSet(app, chart, + analysis, GPXDataSetAxisType.DISTANCE, false, true, calcWithoutGaps); + dataList.add(elevationDataSet); + } + break; + } + case SLOPE: + if (analysis.hasElevationData) { + OrderedLineDataSet slopeDataSet = GpxUiHelper.createGPXSlopeDataSet(app, chart, + analysis, GPXDataSetAxisType.DISTANCE, null, true, true, calcWithoutGaps); + dataList.add(slopeDataSet); + } + break; + case SPEED: { + if (analysis.hasSpeedData) { + OrderedLineDataSet speedDataSet = GpxUiHelper.createGPXSpeedDataSet(app, chart, + analysis, GPXDataSetAxisType.DISTANCE, false, true, calcWithoutGaps); + dataList.add(speedDataSet); + } + break; + } + } + } + if (dataList.size() > 0) { + Collections.sort(dataList, new Comparator() { + @Override + public int compare(OrderedLineDataSet o1, OrderedLineDataSet o2) { + return Float.compare(o1.getPriority(), o2.getPriority()); + } + }); + } + return new ArrayList(dataList); + } + + public static GpxDisplayItem makeGpxDisplayItem(OsmandApplication app, GPXUtilities.GPXFile gpx) { + GpxDisplayItem gpxItem = null; + String groupName = app.getString(R.string.current_route); + GpxDisplayGroup group = app.getSelectedGpxHelper().buildGpxDisplayGroup(gpx, 0, groupName); + if (group != null && group.getModifiableList().size() > 0) { + gpxItem = group.getModifiableList().get(0); + if (gpxItem != null) { + gpxItem.route = true; + } + } + return gpxItem; + } public static class GPXInfo { diff --git a/OsmAnd/src/net/osmand/plus/measurementtool/GraphsCard.java b/OsmAnd/src/net/osmand/plus/measurementtool/GraphsCard.java index 9a5cbeffba..3ae28d83b2 100644 --- a/OsmAnd/src/net/osmand/plus/measurementtool/GraphsCard.java +++ b/OsmAnd/src/net/osmand/plus/measurementtool/GraphsCard.java @@ -1,10 +1,12 @@ package net.osmand.plus.measurementtool; import android.view.View; +import android.view.ViewGroup; import android.widget.ImageView; import android.widget.ProgressBar; import android.widget.TextView; +import androidx.annotation.DrawableRes; import androidx.annotation.NonNull; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; @@ -16,17 +18,25 @@ import com.github.mikephil.charting.data.ChartData; import com.github.mikephil.charting.data.LineData; import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; +import net.osmand.plus.GpxSelectionHelper.GpxDisplayItem; import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; import net.osmand.plus.activities.MapActivity; import net.osmand.plus.activities.SettingsBaseActivity; import net.osmand.plus.helpers.GpxUiHelper; -import net.osmand.plus.helpers.GpxUiHelper.OrderedLineDataSet; -import net.osmand.plus.helpers.GpxUiHelper.GPXDataSetAxisType; +import net.osmand.plus.helpers.GpxUiHelper.LineGraphType; import net.osmand.plus.mapcontextmenu.other.HorizontalSelectionAdapter; +import net.osmand.plus.mapcontextmenu.other.HorizontalSelectionAdapter.HorizontalSelectionAdapterListener; +import net.osmand.plus.mapcontextmenu.other.TrackDetailsMenu; import net.osmand.plus.measurementtool.MeasurementToolFragment.OnUpdateAdditionalInfoListener; +import net.osmand.plus.measurementtool.graph.CommonGraphAdapter; +import net.osmand.plus.measurementtool.graph.CustomGraphAdapter; +import net.osmand.plus.measurementtool.graph.CustomGraphAdapter.LegendViewType; +import net.osmand.plus.measurementtool.graph.BaseGraphAdapter; +import net.osmand.plus.measurementtool.graph.GraphAdapterHelper; import net.osmand.plus.routepreparationmenu.RouteDetailsFragment; import net.osmand.plus.routepreparationmenu.cards.BaseCard; +import net.osmand.plus.routepreparationmenu.cards.RouteStatisticCard; import net.osmand.router.RouteSegmentResult; import net.osmand.util.Algorithms; @@ -36,44 +46,52 @@ import static net.osmand.GPXUtilities.GPXFile; import static net.osmand.plus.mapcontextmenu.other.HorizontalSelectionAdapter.HorizontalSelectionItem; import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; +import java.util.Arrays; import java.util.List; public class GraphsCard extends BaseCard implements OnUpdateAdditionalInfoListener { private static String GRAPH_DATA_GPX_FILE_NAME = "graph_data_tmp"; + private static int INVALID_ID = -1; private MeasurementEditingContext editingCtx; private MeasurementToolFragment fragment; - private GraphType visibleGraphType; + private GraphType visibleType; private List graphTypes = new ArrayList<>(); + private GpxDisplayItem gpxItem; private View commonGraphContainer; private View customGraphContainer; private View messageContainer; - private LineChart commonGraphChart; - private HorizontalBarChart customGraphChart; + private CommonGraphAdapter commonGraphAdapter; + private CustomGraphAdapter customGraphAdapter; private RecyclerView graphTypesMenu; - private enum CommonGraphType { - OVERVIEW(R.string.shared_string_overview, false), - ALTITUDE(R.string.altitude, true), - SLOPE(R.string.shared_string_slope, true), - SPEED(R.string.map_widget_speed, false); + private TrackDetailsMenu trackDetailsMenu; - CommonGraphType(int titleId, boolean canBeCalculated) { + private enum CommonGraphType { + OVERVIEW(R.string.shared_string_overview, true, LineGraphType.ALTITUDE, LineGraphType.SLOPE), + ALTITUDE(R.string.altitude, true, LineGraphType.ALTITUDE), + SLOPE(R.string.shared_string_slope, true, LineGraphType.SLOPE), + SPEED(R.string.map_widget_speed, false, LineGraphType.SPEED); + + CommonGraphType(int titleId, boolean canBeCalculated, LineGraphType ... lineGraphTypes) { this.titleId = titleId; this.canBeCalculated = canBeCalculated; + this.lineGraphTypes = lineGraphTypes; } final int titleId; final boolean canBeCalculated; + final LineGraphType[] lineGraphTypes; } - public GraphsCard(@NonNull MapActivity mapActivity, MeasurementToolFragment fragment) { + public GraphsCard(@NonNull MapActivity mapActivity, + TrackDetailsMenu trackDetailsMenu, + MeasurementToolFragment fragment) { super(mapActivity); + this.trackDetailsMenu = trackDetailsMenu; this.fragment = fragment; } @@ -82,19 +100,27 @@ public class GraphsCard extends BaseCard implements OnUpdateAdditionalInfoListen if (mapActivity == null || fragment == null) return; editingCtx = fragment.getEditingCtx(); + graphTypesMenu = view.findViewById(R.id.graph_types_recycler_view); + graphTypesMenu.setLayoutManager(new LinearLayoutManager(mapActivity, RecyclerView.HORIZONTAL, false)); commonGraphContainer = view.findViewById(R.id.common_graphs_container); customGraphContainer = view.findViewById(R.id.custom_graphs_container); messageContainer = view.findViewById(R.id.message_container); - commonGraphChart = (LineChart) view.findViewById(R.id.line_chart); - customGraphChart = (HorizontalBarChart) view.findViewById(R.id.horizontal_chart); - updateGraphData(); + LineChart lineChart = (LineChart) view.findViewById(R.id.line_chart); + HorizontalBarChart barChart = (HorizontalBarChart) view.findViewById(R.id.horizontal_chart); + commonGraphAdapter = new CommonGraphAdapter(lineChart, true); + customGraphAdapter = new CustomGraphAdapter(barChart, true); - graphTypesMenu = view.findViewById(R.id.graph_types_recycler_view); - graphTypesMenu.setLayoutManager( - new LinearLayoutManager(mapActivity, RecyclerView.HORIZONTAL, false)); + customGraphAdapter.setLegendContainer((ViewGroup) view.findViewById(R.id.route_legend)); + customGraphAdapter.setLayoutChangeListener(new BaseGraphAdapter.LayoutChangeListener() { + @Override + public void onLayoutChanged() { + setLayoutNeeded(); + } + }); - refreshGraphTypesSelectionMenu(); - updateDataView(); + GraphAdapterHelper.bindGraphAdapters(commonGraphAdapter, Arrays.asList((BaseGraphAdapter) customGraphAdapter), (ViewGroup) view); + GraphAdapterHelper.bindToMap(commonGraphAdapter, mapActivity, trackDetailsMenu); + fullUpdate(); } @Override @@ -104,15 +130,29 @@ public class GraphsCard extends BaseCard implements OnUpdateAdditionalInfoListen @Override public void onUpdateAdditionalInfo() { - if (!isRouteCalculating()) { - updateGraphData(); - refreshGraphTypesSelectionMenu(); - } - updateDataView(); + fullUpdate(); } - private void refreshGraphTypesSelectionMenu() { - graphTypesMenu.removeAllViews(); + private void fullUpdate() { + if (!isRouteCalculating()) { + updateData(); + updateVisibleType(); + updateTypesMenu(); + } + updateInfoView(); + } + + private void updateTypesMenu() { + if (!editingCtx.isPointsEnoughToCalculateRoute()) { + graphTypesMenu.setVisibility(View.GONE); + } else { + graphTypesMenu.setVisibility(View.VISIBLE); + graphTypesMenu.removeAllViews(); + fillInTypesMenu(); + } + } + + private void fillInTypesMenu() { OsmandApplication app = getMyApplication(); int activeColorId = nightMode ? R.color.active_color_primary_dark : R.color.active_color_primary_light; final HorizontalSelectionAdapter adapter = new HorizontalSelectionAdapter(app, nightMode); @@ -127,16 +167,16 @@ public class GraphsCard extends BaseCard implements OnUpdateAdditionalInfoListen } } adapter.setItems(items); - String selectedItemKey = visibleGraphType.getTitle(); + String selectedItemKey = visibleType.getTitle(); adapter.setSelectedItemByTitle(selectedItemKey); - adapter.setListener(new HorizontalSelectionAdapter.HorizontalSelectionAdapterListener() { + adapter.setListener(new HorizontalSelectionAdapterListener() { @Override - public void onItemSelected(HorizontalSelectionAdapter.HorizontalSelectionItem item) { + public void onItemSelected(HorizontalSelectionItem item) { adapter.setItems(items); adapter.setSelectedItem(item); - GraphType chosenGraphType = (GraphType) item.getObject(); - if (!isCurrentVisibleType(chosenGraphType)) { - setupVisibleGraphType(chosenGraphType); + GraphType chosenType = (GraphType) item.getObject(); + if (!isCurrentVisibleType(chosenType)) { + changeVisibleType(chosenType); } } }); @@ -144,19 +184,19 @@ public class GraphsCard extends BaseCard implements OnUpdateAdditionalInfoListen adapter.notifyDataSetChanged(); } - private void setupVisibleGraphType(GraphType type) { - visibleGraphType = type; - updateDataView(); + private void changeVisibleType(GraphType type) { + visibleType = type; + updateInfoView(); } private boolean isCurrentVisibleType(GraphType type) { - if (visibleGraphType != null && type != null) { - return Algorithms.objectEquals(visibleGraphType.getTitle(), type.getTitle()); + if (visibleType != null && type != null) { + return Algorithms.objectEquals(visibleType.getTitle(), type.getTitle()); } return false; } - private GraphType getFirstAvailableGraphType() { + private GraphType getFirstAvailableType() { for (GraphType type : graphTypes) { if (type.isAvailable()) { return type; @@ -165,189 +205,146 @@ public class GraphsCard extends BaseCard implements OnUpdateAdditionalInfoListen return null; } - private void updateDataView() { - if (isRouteCalculating()) { - showProgressMessage(); - } else if (visibleGraphType.hasData()) { + private void updateInfoView() { + hideAll(); + if (!editingCtx.isPointsEnoughToCalculateRoute()) { + showMessage(app.getString(R.string.message_you_need_add_two_points_to_show_graphs)); + } else if (isRouteCalculating()) { + showMessage(app.getString(R.string.message_graph_will_be_available_after_recalculation), true); + } else if (visibleType.hasData()) { showGraph(); - } else if (visibleGraphType.canBeCalculated()) { - showMessage(); + } else if (visibleType.canBeCalculated()) { + showMessage(app.getString(R.string.message_need_calculate_route_before_show_graph, + visibleType.getTitle()), R.drawable.ic_action_altitude_average, + app.getString(R.string.route_between_points), new View.OnClickListener() { + @Override + public void onClick(View v) { + fragment.startSnapToRoad(false); + } + }); } } - private void showProgressMessage() { + private void hideAll() { commonGraphContainer.setVisibility(View.GONE); customGraphContainer.setVisibility(View.GONE); + messageContainer.setVisibility(View.GONE); + } + + private void showMessage(String text) { + showMessage(text, INVALID_ID, false, null, null); + } + + private void showMessage(String text, @DrawableRes int iconResId, String btnTitle, View.OnClickListener btnListener) { + showMessage(text, iconResId, false, btnTitle, btnListener); + } + + private void showMessage(String text, boolean showProgressBar) { + showMessage(text, INVALID_ID, showProgressBar, null, null); + } + + private void showMessage(@NonNull String text, + @DrawableRes int iconResId, + boolean showProgressBar, + String btnTitle, + View.OnClickListener btnListener) { messageContainer.setVisibility(View.VISIBLE); TextView tvMessage = messageContainer.findViewById(R.id.message_text); + tvMessage.setText(text); ImageView icon = messageContainer.findViewById(R.id.message_icon); + if (iconResId != INVALID_ID) { + icon.setVisibility(View.VISIBLE); + icon.setImageResource(iconResId); + } else { + icon.setVisibility(View.GONE); + } ProgressBar pb = messageContainer.findViewById(R.id.progress_bar); - pb.setVisibility(View.VISIBLE); - icon.setVisibility(View.GONE); - tvMessage.setText(R.string.message_graph_will_be_available_after_recalculation); + pb.setVisibility(showProgressBar ? View.VISIBLE : View.GONE); + View btnContainer = messageContainer.findViewById(R.id.btn_container); + if (btnTitle != null) { + TextView tvBtnTitle = btnContainer.findViewById(R.id.btn_text); + tvBtnTitle.setText(btnTitle); + btnContainer.setVisibility(View.VISIBLE); + } else { + btnContainer.setVisibility(View.GONE); + } + if (btnListener != null) { + btnContainer.setOnClickListener(btnListener); + } } private void showGraph() { - if (visibleGraphType.isCustom()) { - customGraphChart.clear(); - commonGraphContainer.setVisibility(View.GONE); + if (visibleType.isCustom()) { + CustomGraphType customGraphType = (CustomGraphType) visibleType; customGraphContainer.setVisibility(View.VISIBLE); - messageContainer.setVisibility(View.GONE); - prepareCustomGraphView((BarData) visibleGraphType.getGraphData()); + customGraphAdapter.setLegendViewType(LegendViewType.ONE_ELEMENT); + customGraphAdapter.fullUpdate((BarData) customGraphType.getGraphData(), customGraphType.getStatistics()); } else { - commonGraphChart.clear(); commonGraphContainer.setVisibility(View.VISIBLE); - customGraphContainer.setVisibility(View.GONE); - messageContainer.setVisibility(View.GONE); - prepareCommonGraphView((LineData) visibleGraphType.getGraphData()); + customGraphAdapter.setLegendViewType(LegendViewType.GONE); + commonGraphAdapter.fullUpdate((LineData) visibleType.getGraphData(), gpxItem); } } - private void showMessage() { - commonGraphContainer.setVisibility(View.GONE); - customGraphContainer.setVisibility(View.GONE); - messageContainer.setVisibility(View.VISIBLE); - TextView tvMessage = messageContainer.findViewById(R.id.message_text); - ImageView icon = messageContainer.findViewById(R.id.message_icon); - ProgressBar pb = messageContainer.findViewById(R.id.progress_bar); - pb.setVisibility(View.GONE); - icon.setVisibility(View.VISIBLE); - tvMessage.setText(app.getString( - R.string.message_need_calculate_route_before_show_graph, - visibleGraphType.getTitle())); - icon.setImageResource(R.drawable.ic_action_altitude_average); - } - - private void prepareCommonGraphView(LineData data) { - GpxUiHelper.setupGPXChart(commonGraphChart, 4, 24f, 16f, !nightMode, true); - commonGraphChart.setData(data); - } - - private void prepareCustomGraphView(BarData data) { - OsmandApplication app = getMyApplication(); - if (app == null) return; - - GpxUiHelper.setupHorizontalGPXChart(app, customGraphChart, 5, 9, 24, true, nightMode); - customGraphChart.setExtraRightOffset(16); - customGraphChart.setExtraLeftOffset(16); - customGraphChart.setData(data); - } - - private void updateGraphData() { + private void updateData() { graphTypes.clear(); OsmandApplication app = getMyApplication(); - GPXTrackAnalysis analysis = createGpxTrackAnalysis(); + GPXFile gpxFile = getGpxFile(); + GPXTrackAnalysis analysis = gpxFile != null ? gpxFile.getAnalysis(0) : null; + gpxItem = gpxFile != null ? GpxUiHelper.makeGpxDisplayItem(app, gpxFile) : null; + if (gpxItem != null) { + trackDetailsMenu.setGpxItem(gpxItem); + } // update common graph data for (CommonGraphType commonType : CommonGraphType.values()) { - List dataSets = getDataSets(commonType, commonGraphChart, analysis); - LineData data = null; - if (!Algorithms.isEmpty(dataSets)) { - data = new LineData(dataSets); - } + List dataSets = GpxUiHelper.getDataSets(commonGraphAdapter.getChart(), + app, analysis, false, commonType.lineGraphTypes); + LineData data = !Algorithms.isEmpty(dataSets) ? new LineData(dataSets) : null; String title = app.getString(commonType.titleId); - graphTypes.add(new GraphType(title, false, commonType.canBeCalculated, data)); + graphTypes.add(new GraphType(title, commonType.canBeCalculated, data)); } // update custom graph data - List routeSegments = editingCtx.getAllRouteSegments(); - List routeStatistics = calculateRouteStatistics(routeSegments); + List routeStatistics = calculateRouteStatistics(); if (analysis != null && routeStatistics != null) { for (RouteStatistics statistics : routeStatistics) { String title = SettingsBaseActivity.getStringRouteInfoPropertyValue(app, statistics.name); BarData data = null; if (!Algorithms.isEmpty(statistics.elements)) { - data = GpxUiHelper.buildStatisticChart( - app, customGraphChart, statistics, analysis, true, nightMode); + data = GpxUiHelper.buildStatisticChart(app, customGraphAdapter.getChart(), + statistics, analysis, true, nightMode); } - graphTypes.add(new GraphType(title, true, false, data)); + graphTypes.add(new CustomGraphType(title, false, data, statistics)); } } + } - // update current visible graph type - if (visibleGraphType == null) { - visibleGraphType = getFirstAvailableGraphType(); + private void updateVisibleType() { + if (visibleType == null) { + visibleType = getFirstAvailableType(); } else { for (GraphType type : graphTypes) { if (isCurrentVisibleType(type)) { - visibleGraphType = type.isAvailable() ? type : getFirstAvailableGraphType(); + visibleType = type.isAvailable() ? type : getFirstAvailableType(); break; } } } } - private List getDataSets(CommonGraphType type, LineChart chart, GPXTrackAnalysis analysis) { - List dataSets = new ArrayList<>(); - if (chart != null && analysis != null) { - OsmandApplication app = getMyApplication(); - switch (type) { - case OVERVIEW: { - List dataList = new ArrayList<>(); - if (analysis.hasSpeedData) { - OrderedLineDataSet speedDataSet = GpxUiHelper.createGPXSpeedDataSet(app, chart, - analysis, GPXDataSetAxisType.DISTANCE, true, true, false); - dataList.add(speedDataSet); - } - if (analysis.hasElevationData) { - OrderedLineDataSet elevationDataSet = GpxUiHelper.createGPXElevationDataSet(app, chart, - analysis, GPXDataSetAxisType.DISTANCE, false, true, false); - dataList.add(elevationDataSet); - OrderedLineDataSet slopeDataSet = GpxUiHelper.createGPXSlopeDataSet(app, chart, - analysis, GPXDataSetAxisType.DISTANCE, null, true, true, false); - dataList.add(slopeDataSet); - } - if (dataList.size() > 0) { - Collections.sort(dataList, new Comparator() { - @Override - public int compare(OrderedLineDataSet o1, OrderedLineDataSet o2) { - return Float.compare(o1.getPriority(), o2.getPriority()); - } - }); - } - dataSets.addAll(dataList); - break; - } - case ALTITUDE: { - if (analysis.hasElevationData) { - OrderedLineDataSet elevationDataSet = GpxUiHelper.createGPXElevationDataSet(app, chart, - analysis, GPXDataSetAxisType.DISTANCE, false, true, false);//calcWithoutGaps); - dataSets.add(elevationDataSet); - } - break; - } - case SLOPE: - if (analysis.hasElevationData) { - OrderedLineDataSet slopeDataSet = GpxUiHelper.createGPXSlopeDataSet(app, chart, - analysis, GPXDataSetAxisType.DISTANCE, null, true, true, false); - dataSets.add(slopeDataSet); - } - break; - case SPEED: { - if (analysis.hasSpeedData) { - OrderedLineDataSet speedDataSet = GpxUiHelper.createGPXSpeedDataSet(app, chart, - analysis, GPXDataSetAxisType.DISTANCE, false, true, false);//calcWithoutGaps); - dataSets.add(speedDataSet); - } - break; - } - } - } - return dataSets; - } - - private GPXTrackAnalysis createGpxTrackAnalysis() { - GPXFile gpx; - if (editingCtx.getGpxData() != null) { - gpx = editingCtx.getGpxData().getGpxFile(); + private GPXFile getGpxFile() { + if (fragment.isTrackReadyToCalculate()) { + return editingCtx.exportRouteAsGpx(GRAPH_DATA_GPX_FILE_NAME); } else { - gpx = editingCtx.exportRouteAsGpx(GRAPH_DATA_GPX_FILE_NAME); + GpxData gpxData = editingCtx.getGpxData(); + return gpxData != null ? gpxData.getGpxFile() : null; } - return gpx != null ? gpx.getAnalysis(0) : null; } - private List calculateRouteStatistics(List route) { + private List calculateRouteStatistics() { OsmandApplication app = getMyApplication(); + List route = editingCtx.getAllRouteSegments(); if (route == null || app == null) return null; return RouteDetailsFragment.calculateRouteStatistics(app, route, nightMode); } @@ -356,15 +353,13 @@ public class GraphsCard extends BaseCard implements OnUpdateAdditionalInfoListen return fragment.isProgressBarVisible(); } - private static class GraphType { + private class GraphType { private String title; - private boolean isCustom; private boolean canBeCalculated; private ChartData graphData; - public GraphType(String title, boolean isCustom, boolean canBeCalculated, ChartData graphData) { + public GraphType(String title, boolean canBeCalculated, ChartData graphData) { this.title = title; - this.isCustom = isCustom; this.canBeCalculated = canBeCalculated; this.graphData = graphData; } @@ -374,11 +369,15 @@ public class GraphsCard extends BaseCard implements OnUpdateAdditionalInfoListen } public boolean isCustom() { - return isCustom; + return this instanceof CustomGraphType; } public boolean isAvailable() { - return hasData() || canBeCalculated(); + return isPointsCountEnoughToCalculateRoute() && (hasData() || canBeCalculated()); + } + + private boolean isPointsCountEnoughToCalculateRoute() { + return editingCtx.getPointsCount() >= 2; } public boolean canBeCalculated() { @@ -393,4 +392,18 @@ public class GraphsCard extends BaseCard implements OnUpdateAdditionalInfoListen return graphData; } } + + private class CustomGraphType extends GraphType { + + private RouteStatistics statistics; + + public CustomGraphType(String title, boolean canBeCalculated, ChartData graphData, RouteStatistics statistics) { + super(title, canBeCalculated, graphData); + this.statistics = statistics; + } + + public RouteStatistics getStatistics() { + return statistics; + } + } } diff --git a/OsmAnd/src/net/osmand/plus/measurementtool/MeasurementEditingContext.java b/OsmAnd/src/net/osmand/plus/measurementtool/MeasurementEditingContext.java index 307d89d501..ad6c5f4c4f 100644 --- a/OsmAnd/src/net/osmand/plus/measurementtool/MeasurementEditingContext.java +++ b/OsmAnd/src/net/osmand/plus/measurementtool/MeasurementEditingContext.java @@ -313,6 +313,10 @@ public class MeasurementEditingContext { return before.points.size(); } + public boolean isPointsEnoughToCalculateRoute() { + return getPointsCount() >= 2; + } + public List getAllRouteSegments() { List allSegments = new ArrayList<>(); for (Pair key : getOrderedRoadSegmentDataKeys()) { diff --git a/OsmAnd/src/net/osmand/plus/measurementtool/MeasurementToolFragment.java b/OsmAnd/src/net/osmand/plus/measurementtool/MeasurementToolFragment.java index c40e7b70ca..9e6ecef9e3 100644 --- a/OsmAnd/src/net/osmand/plus/measurementtool/MeasurementToolFragment.java +++ b/OsmAnd/src/net/osmand/plus/measurementtool/MeasurementToolFragment.java @@ -52,6 +52,7 @@ import net.osmand.plus.activities.TrackActivity; import net.osmand.plus.base.BaseOsmAndFragment; import net.osmand.plus.base.ContextMenuFragment.MenuState; import net.osmand.plus.helpers.AndroidUiHelper; +import net.osmand.plus.mapcontextmenu.other.TrackDetailsMenu; import net.osmand.plus.measurementtool.GpxApproximationFragment.GpxApproximationFragmentListener; import net.osmand.plus.measurementtool.GpxData.ActionType; import net.osmand.plus.measurementtool.OptionsBottomSheetDialogFragment.OptionsFragmentListener; @@ -116,8 +117,10 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route private TextView distanceToCenterTv; private String pointsSt; private View additionalInfoContainer; - private ViewGroup additionalInfoCardsContainer; - private BaseCard visibleAdditionalInfoCard; + private ViewGroup cardsContainer; + private BaseCard visibleCard; + private PointsCard pointsCard; + private GraphsCard graphsCard; private LinearLayout customRadioButton; private View mainView; private ImageView upDownBtn; @@ -145,6 +148,7 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route private int cachedMapPosition; private MeasurementEditingContext editingCtx = new MeasurementEditingContext(); + private GraphDetailsMenu detailsMenu; private LatLon initialPoint; @@ -164,6 +168,19 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route GRAPH } + private class GraphDetailsMenu extends TrackDetailsMenu { + + @Override + protected int getFragmentWidth() { + return mainView.getWidth(); + } + + @Override + protected int getFragmentHeight() { + return mainView.getHeight(); + } + } + private void setEditingCtx(MeasurementEditingContext editingCtx) { this.editingCtx = editingCtx; } @@ -257,8 +274,9 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route mainView = view.findViewById(R.id.main_view); AndroidUtils.setBackground(mapActivity, mainView, nightMode, R.drawable.bg_bottom_menu_light, R.drawable.bg_bottom_menu_dark); + detailsMenu = new GraphDetailsMenu(); additionalInfoContainer = mainView.findViewById(R.id.additional_info_container); - additionalInfoCardsContainer = mainView.findViewById(R.id.cards_container); + cardsContainer = mainView.findViewById(R.id.cards_container); if (portrait) { customRadioButton = mainView.findViewById(R.id.custom_radio_buttons); @@ -282,6 +300,8 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route } }); } + pointsCard = new PointsCard(mapActivity, this); + graphsCard = new GraphsCard(mapActivity, detailsMenu, this); if (progressBarVisible) { showProgressBar(); @@ -517,29 +537,28 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route if (!additionalInfoExpanded || !isCurrentAdditionalInfoType(type)) { MapActivity ma = getMapActivity(); if (ma == null) return; - currentAdditionalInfoType = type; - updateUpDownBtn(); + OsmandApplication app = ma.getMyApplication(); - BaseCard additionalInfoCard = null; if (AdditionalInfoType.POINTS == type) { - additionalInfoCard = new PointsCard(ma, this); + visibleCard = pointsCard; UiUtilities.updateCustomRadioButtons(app, customRadioButton, nightMode, START); } else if (AdditionalInfoType.GRAPH == type) { - additionalInfoCard = new GraphsCard(ma, this); + visibleCard = graphsCard; UiUtilities.updateCustomRadioButtons(app, customRadioButton, nightMode, END); } - if (additionalInfoCard != null) { - visibleAdditionalInfoCard = additionalInfoCard; - additionalInfoCardsContainer.removeAllViews(); - additionalInfoCardsContainer.addView(additionalInfoCard.build(ma)); - additionalInfoExpanded = true; - } + cardsContainer.removeAllViews(); + View cardView = visibleCard.getView() != null ? visibleCard.getView() : visibleCard.build(ma); + cardsContainer.addView(cardView); + + currentAdditionalInfoType = type; + additionalInfoExpanded = true; + updateUpDownBtn(); } } private void updateAdditionalInfoView() { - if (visibleAdditionalInfoCard instanceof OnUpdateAdditionalInfoListener) { - ((OnUpdateAdditionalInfoListener) visibleAdditionalInfoCard).onUpdateAdditionalInfo(); + if (visibleCard instanceof OnUpdateAdditionalInfoListener) { + ((OnUpdateAdditionalInfoListener) visibleCard).onUpdateAdditionalInfo(); } } @@ -597,6 +616,7 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route super.onResume(); MapActivity mapActivity = getMapActivity(); if (mapActivity != null) { + detailsMenu.setMapActivity(mapActivity); mapActivity.getMapLayers().getMapControlsLayer().addThemeInfoProviderTag(TAG); mapActivity.getMapLayers().getMapControlsLayer().showMapControlsIfHidden(); cachedMapPosition = mapActivity.getMapView().getMapPosition(); @@ -612,6 +632,8 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route if (mapActivity != null) { mapActivity.getMapLayers().getMapControlsLayer().removeThemeInfoProviderTag(TAG); } + detailsMenu.onDismiss(); + detailsMenu.setMapActivity(null); setMapPosition(cachedMapPosition); } @@ -687,7 +709,7 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route } } - private void startSnapToRoad(boolean rememberPreviousTitle) { + public void startSnapToRoad(boolean rememberPreviousTitle) { MapActivity mapActivity = getMapActivity(); if (mapActivity != null) { if (rememberPreviousTitle) { @@ -1219,7 +1241,7 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route final ApplicationMode appMode = editingCtx.getAppMode(); if (mapActivity != null) { Drawable icon; - if (editingCtx.isTrackSnappedToRoad() || editingCtx.isNewData() || approximationApplied) { + if (isTrackReadyToCalculate()) { if (appMode == MeasurementEditingContext.DEFAULT_APP_MODE) { icon = getActiveIcon(R.drawable.ic_action_split_interval); } else { @@ -1234,6 +1256,10 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route } } + public boolean isTrackReadyToCalculate() { + return editingCtx.isTrackSnappedToRoad() || editingCtx.isNewData() || approximationApplied; + } + private void hideSnapToRoadIcon() { MapActivity mapActivity = getMapActivity(); if (mapActivity != null) { diff --git a/OsmAnd/src/net/osmand/plus/measurementtool/graph/BaseGraphAdapter.java b/OsmAnd/src/net/osmand/plus/measurementtool/graph/BaseGraphAdapter.java new file mode 100644 index 0000000000..dc83460a3f --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/measurementtool/graph/BaseGraphAdapter.java @@ -0,0 +1,82 @@ +package net.osmand.plus.measurementtool.graph; + +import android.view.MotionEvent; + +import com.github.mikephil.charting.charts.Chart; +import com.github.mikephil.charting.data.ChartData; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.listener.ChartTouchListener; + +import net.osmand.plus.OsmandApplication; + +public abstract class BaseGraphAdapter { + + private Highlight lastKnownHighlight; + protected CHART mChart; + protected CHART_DATA mChartData; + protected DATA mAdditionalData; + protected boolean usedOnMap; + + public BaseGraphAdapter(CHART chart, boolean usedOnMap) { + this.mChart = chart; + this.usedOnMap = usedOnMap; + prepareCharterView(); + } + + protected void prepareCharterView() { + mChart.setExtraRightOffset(16); + mChart.setExtraLeftOffset(16); + } + + public CHART getChart() { + return mChart; + } + + protected void updateHighlight() { + highlight(lastKnownHighlight); + } + + public void highlight(Highlight h) { + this.lastKnownHighlight = h; + } + + public void fullUpdate(CHART_DATA chartData, DATA data) { + updateData(chartData, data); + updateView(); + } + + public void updateData(CHART_DATA chartData, DATA data) { + this.mChartData = chartData; + this.mAdditionalData = data; + } + + public abstract void updateView(); + + protected boolean isNightMode() { + OsmandApplication app = getMyApplication(); + if (app != null) { + return usedOnMap ? app.getDaynightHelper().isNightModeForMapControls() + : !app.getSettings().isLightContent(); + } + return false; + } + + protected OsmandApplication getMyApplication() { + return (OsmandApplication) mChart.getContext().getApplicationContext(); + } + + public interface ExternalValueSelectedListener { + void onValueSelected(Entry e, Highlight h); + void onNothingSelected(); + } + + public interface ExternalGestureListener { + void onChartGestureStart(MotionEvent me, ChartTouchListener.ChartGesture lastPerformedGesture); + void onChartGestureEnd(MotionEvent me, ChartTouchListener.ChartGesture lastPerformedGesture, boolean hasTranslated); + } + + public interface LayoutChangeListener { + void onLayoutChanged(); + } +} diff --git a/OsmAnd/src/net/osmand/plus/measurementtool/graph/CommonGraphAdapter.java b/OsmAnd/src/net/osmand/plus/measurementtool/graph/CommonGraphAdapter.java new file mode 100644 index 0000000000..693b5669e7 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/measurementtool/graph/CommonGraphAdapter.java @@ -0,0 +1,152 @@ +package net.osmand.plus.measurementtool.graph; + +import android.graphics.Matrix; +import android.view.MotionEvent; + +import com.github.mikephil.charting.charts.LineChart; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.data.LineData; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.listener.ChartTouchListener; +import com.github.mikephil.charting.listener.OnChartGestureListener; +import com.github.mikephil.charting.listener.OnChartValueSelectedListener; + +import net.osmand.plus.GpxSelectionHelper.GpxDisplayItem; +import net.osmand.plus.helpers.GpxUiHelper; + +import java.util.HashMap; +import java.util.Map; + +public class CommonGraphAdapter extends BaseGraphAdapter { + + private Highlight highlight; + private Map externalValueSelectedListeners = new HashMap<>(); + private ExternalGestureListener externalGestureListener; + + public CommonGraphAdapter(LineChart chart, boolean usedOnMap) { + super(chart, usedOnMap); + } + + @Override + protected void prepareCharterView() { + super.prepareCharterView(); + + mChart.setOnChartValueSelectedListener(new OnChartValueSelectedListener() { + @Override + public void onValueSelected(Entry e, Highlight h) { + highlight = h; + for (ExternalValueSelectedListener listener : externalValueSelectedListeners.values()) { + listener.onValueSelected(e, h); + } + } + + @Override + public void onNothingSelected() { + for (ExternalValueSelectedListener listener : externalValueSelectedListeners.values()) { + listener.onNothingSelected(); + } + } + }); + + mChart.setOnChartGestureListener(new OnChartGestureListener() { + boolean hasTranslated = false; + float highlightDrawX = -1; + + @Override + public void onChartGestureStart(MotionEvent me, ChartTouchListener.ChartGesture lastPerformedGesture) { + hasTranslated = false; + if (mChart.getHighlighted() != null && mChart.getHighlighted().length > 0) { + highlightDrawX = mChart.getHighlighted()[0].getDrawX(); + } else { + highlightDrawX = -1; + } + if (externalGestureListener != null) { + externalGestureListener.onChartGestureStart(me, lastPerformedGesture); + } + } + + @Override + public void onChartGestureEnd(MotionEvent me, ChartTouchListener.ChartGesture lastPerformedGesture) { + GpxDisplayItem gpxItem = getGpxItem(); + gpxItem.chartMatrix = new Matrix(mChart.getViewPortHandler().getMatrixTouch()); + Highlight[] highlights = mChart.getHighlighted(); + if (highlights != null && highlights.length > 0) { + gpxItem.chartHighlightPos = highlights[0].getX(); + } else { + gpxItem.chartHighlightPos = -1; + } + if (externalGestureListener != null) { + externalGestureListener.onChartGestureEnd(me, lastPerformedGesture, hasTranslated); + } + } + + @Override + public void onChartLongPressed(MotionEvent me) { + } + + @Override + public void onChartDoubleTapped(MotionEvent me) { + } + + @Override + public void onChartSingleTapped(MotionEvent me) { + } + + @Override + public void onChartFling(MotionEvent me1, MotionEvent me2, float velocityX, float velocityY) { + } + + @Override + public void onChartScale(MotionEvent me, float scaleX, float scaleY) { + } + + @Override + public void onChartTranslate(MotionEvent me, float dX, float dY) { + hasTranslated = true; + if (highlightDrawX != -1) { + Highlight h = mChart.getHighlightByTouchPoint(highlightDrawX, 0f); + if (h != null) { + /* + ILineDataSet set = mChart.getLineData().getDataSetByIndex(h.getDataSetIndex()); + if (set != null && set.isHighlightEnabled()) { + Entry e = set.getEntryForXValue(h.getX(), h.getY()); + MPPointD pix = mChart.getTransformer(set.getAxisDependency()).getPixelForValues(e.getX(), e.getY()); + h.setDraw((float) pix.x, (float) pix.y); + } + */ + mChart.highlightValue(h, true); + } + } + } + }); + } + + public void addValueSelectedListener(String key, ExternalValueSelectedListener listener) { + this.externalValueSelectedListeners.put(key, listener); + } + + public void removeValueSelectedListener(String key) { + this.externalValueSelectedListeners.remove(key); + } + + public void setExternalGestureListener(ExternalGestureListener listener) { + this.externalGestureListener = listener; + } + + @Override + public void updateView() { + GpxUiHelper.setupGPXChart(mChart, 4, 24f, 16f, !isNightMode(), true); + mChart.setData(mChartData); + updateHighlight(); + } + + @Override + public void highlight(Highlight h) { + super.highlight(h); + mChart.highlightValue(highlight); + } + + public GpxDisplayItem getGpxItem() { + return mAdditionalData; + } +} diff --git a/OsmAnd/src/net/osmand/plus/measurementtool/graph/CustomGraphAdapter.java b/OsmAnd/src/net/osmand/plus/measurementtool/graph/CustomGraphAdapter.java new file mode 100644 index 0000000000..aded2e0fbe --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/measurementtool/graph/CustomGraphAdapter.java @@ -0,0 +1,187 @@ +package net.osmand.plus.measurementtool.graph; + +import android.graphics.drawable.Drawable; +import android.text.Spannable; +import android.text.SpannableStringBuilder; +import android.text.style.StyleSpan; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + +import androidx.core.graphics.ColorUtils; + +import com.github.mikephil.charting.charts.HorizontalBarChart; +import com.github.mikephil.charting.data.BarData; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.listener.OnChartValueSelectedListener; + +import net.osmand.AndroidUtils; +import net.osmand.plus.OsmAndFormatter; +import net.osmand.plus.OsmandApplication; +import net.osmand.plus.R; +import net.osmand.plus.activities.SettingsNavigationActivity; +import net.osmand.plus.helpers.CustomBarChartRenderer; +import net.osmand.plus.helpers.GpxUiHelper; +import net.osmand.router.RouteStatisticsHelper; +import net.osmand.router.RouteStatisticsHelper.RouteStatistics; +import net.osmand.router.RouteStatisticsHelper.RouteSegmentAttribute; +import net.osmand.util.Algorithms; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class CustomGraphAdapter extends BaseGraphAdapter { + + private static final int MINIMUM_CONTRAST_RATIO = 3; + + private String selectedPropertyName; + private ViewGroup legendContainer; + private LegendViewType legendViewType; + private LayoutChangeListener layoutChangeListener; + + public enum LegendViewType { + ONE_ELEMENT, + ALL_AS_LIST, + GONE + } + + public CustomGraphAdapter(HorizontalBarChart chart, boolean usedOnMap) { + super(chart, usedOnMap); + } + + @Override + protected void prepareCharterView() { + super.prepareCharterView(); + legendViewType = LegendViewType.GONE; + mChart.setRenderer(new CustomBarChartRenderer(mChart)); + mChart.setOnChartValueSelectedListener(new OnChartValueSelectedListener() { + @Override + public void onValueSelected(Entry e, Highlight h) { + if (getStatistics() == null) return; + + List elems = getStatistics().elements; + int i = h.getStackIndex(); + if (i >= 0 && elems.size() > i) { + selectedPropertyName = elems.get(i).getPropertyName(); + updateLegend(); + } + } + + @Override + public void onNothingSelected() { + selectedPropertyName = null; + updateLegend(); + } + }); + } + + @Override + public void updateView() { + mChart.clear(); + GpxUiHelper.setupHorizontalGPXChart(getMyApplication(), mChart, 5, 9, 24, true, isNightMode()); + mChart.setData(mChartData); + updateHighlight(); + updateLegend(); + } + + public void setLegendContainer(ViewGroup legendContainer) { + this.legendContainer = legendContainer; + } + + public void setLegendViewType(LegendViewType legendViewType) { + this.legendViewType = legendViewType; + } + + public void setLayoutChangeListener(LayoutChangeListener layoutChangeListener) { + this.layoutChangeListener = layoutChangeListener; + } + + public void highlight(Highlight h) { + super.highlight(h); + Highlight bh = h != null ? mChart.getHighlighter().getHighlight(1, h.getXPx()) : null; + if (bh != null) { + bh.setDraw(h.getXPx(), 0); + } + mChart.highlightValue(bh, true); + } + + private void updateLegend() { + if (legendContainer == null) return; + + legendContainer.removeAllViews(); + attachLegend(); + if (layoutChangeListener != null) { + layoutChangeListener.onLayoutChanged(); + } + } + + private void attachLegend() { + if (getSegmentsList() == null) return; + + switch (legendViewType) { + case ONE_ELEMENT: + for (RouteSegmentAttribute segment : getSegmentsList()) { + if (segment.getPropertyName().equals(selectedPropertyName)) { + attachLegend(Arrays.asList(segment), null); + break; + } + } + break; + case ALL_AS_LIST: + attachLegend(getSegmentsList(), selectedPropertyName); + break; + } + } + + private void attachLegend(List list, + String propertyNameToFullSpan) { + OsmandApplication app = getMyApplication(); + LayoutInflater inflater = LayoutInflater.from(app); + for (RouteStatisticsHelper.RouteSegmentAttribute segment : list) { + View view = inflater.inflate(R.layout.route_details_legend, legendContainer, false); + int segmentColor = segment.getColor(); + Drawable circle = app.getUIUtilities().getPaintedIcon(R.drawable.ic_action_circle, segmentColor); + ImageView legendIcon = (ImageView) view.findViewById(R.id.legend_icon_color); + legendIcon.setImageDrawable(circle); + double contrastRatio = ColorUtils.calculateContrast(segmentColor, + AndroidUtils.getColorFromAttr(app, R.attr.card_and_list_background_basic)); + if (contrastRatio < MINIMUM_CONTRAST_RATIO) { + legendIcon.setBackgroundResource(AndroidUtils.resolveAttribute(app, R.attr.bg_circle_contour)); + } + String propertyName = segment.getUserPropertyName(); + String name = SettingsNavigationActivity.getStringPropertyName(app, propertyName, propertyName.replaceAll("_", " ")); + boolean selected = segment.getPropertyName().equals(propertyNameToFullSpan); + Spannable text = getSpanLegend(name, segment, selected); + TextView legend = (TextView) view.findViewById(R.id.legend_text); + legend.setText(text); + + legendContainer.addView(view); + } + } + + private Spannable getSpanLegend(String title, + RouteSegmentAttribute segment, + boolean fullSpan) { + String formattedDistance = OsmAndFormatter.getFormattedDistance(segment.getDistance(), getMyApplication()); + title = Algorithms.capitalizeFirstLetter(title); + SpannableStringBuilder spannable = new SpannableStringBuilder(title); + spannable.append(": "); + int startIndex = fullSpan ? -0 : spannable.length(); + spannable.append(formattedDistance); + spannable.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), + startIndex, spannable.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + return spannable; + } + + private List getSegmentsList() { + return getStatistics() != null ? new ArrayList<>(getStatistics().partition.values()) : null; + } + + private RouteStatistics getStatistics() { + return mAdditionalData; + } +} diff --git a/OsmAnd/src/net/osmand/plus/measurementtool/graph/GraphAdapterHelper.java b/OsmAnd/src/net/osmand/plus/measurementtool/graph/GraphAdapterHelper.java new file mode 100644 index 0000000000..efacad9d50 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/measurementtool/graph/GraphAdapterHelper.java @@ -0,0 +1,145 @@ +package net.osmand.plus.measurementtool.graph; + +import android.annotation.SuppressLint; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; + +import com.github.mikephil.charting.charts.BarChart; +import com.github.mikephil.charting.charts.LineChart; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.listener.ChartTouchListener; + +import net.osmand.plus.OsmandApplication; +import net.osmand.plus.activities.MapActivity; +import net.osmand.plus.mapcontextmenu.other.TrackDetailsMenu; + +import java.util.List; + +public class GraphAdapterHelper { + + public static final String BIND_GRAPH_ADAPTERS_KEY = "bind_graph_adapters_key"; + public static final String BIND_TO_MAP_KEY = "bind_to_map_key"; + + public static void bindGraphAdapters(final CommonGraphAdapter mainGraphAdapter, + final List otherGraphAdapters, + final ViewGroup mainView) { + if (mainGraphAdapter == null || mainGraphAdapter.getChart() == null + || otherGraphAdapters == null || otherGraphAdapters.size() == 0) { + return; + } + + final LineChart mainChart = mainGraphAdapter.getChart(); + View.OnTouchListener mainChartTouchListener = new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent ev) { + if (mainView != null) { + mainView.requestDisallowInterceptTouchEvent(true); + } + for (BaseGraphAdapter adapter : otherGraphAdapters) { + if (adapter.getChart() != null) { + final MotionEvent event = MotionEvent.obtainNoHistory(ev); + event.setSource(0); + adapter.getChart().dispatchTouchEvent(event); + } + } + return false; + } + }; + mainChart.setOnTouchListener(mainChartTouchListener); + + mainGraphAdapter.addValueSelectedListener(BIND_GRAPH_ADAPTERS_KEY, + new CommonGraphAdapter.ExternalValueSelectedListener() { + @Override + public void onValueSelected(Entry e, Highlight h) { + for (BaseGraphAdapter adapter : otherGraphAdapters) { + adapter.highlight(h); + } + } + + @Override + public void onNothingSelected() { + for (BaseGraphAdapter adapter : otherGraphAdapters) { + adapter.highlight(null); + } + } + } + ); + + View.OnTouchListener otherChartsTouchListener = new View.OnTouchListener() { + @SuppressLint("ClickableViewAccessibility") + @Override + public boolean onTouch(View v, MotionEvent ev) { + if (ev.getSource() != 0) { + final MotionEvent event = MotionEvent.obtainNoHistory(ev); + event.setSource(0); + mainChart.dispatchTouchEvent(event); + return true; + } + return false; + } + }; + + for (BaseGraphAdapter adapter : otherGraphAdapters) { + if (adapter.getChart() != null) { + if (adapter.getChart() instanceof BarChart) { + // maybe we should find min and max axis from all charters + BarChart barChart = (BarChart) adapter.getChart(); + barChart.getAxisRight().setAxisMinimum(mainChart.getXChartMin()); + barChart.getAxisRight().setAxisMaximum(mainChart.getXChartMax()); + barChart.setHighlightPerDragEnabled(false); + barChart.setHighlightPerTapEnabled(false); + } + adapter.getChart().setOnTouchListener(otherChartsTouchListener); + } + } + } + + public static void bindToMap(final CommonGraphAdapter graphAdapter, + final MapActivity mapActivity, + final TrackDetailsMenu detailsMenu) { + graphAdapter.addValueSelectedListener(BIND_TO_MAP_KEY, + new CommonGraphAdapter.ExternalValueSelectedListener() { + + @Override + public void onValueSelected(Entry e, Highlight h) { + refreshChart(mapActivity, graphAdapter.getChart(), detailsMenu, false); + } + + @Override + public void onNothingSelected() { + } + }); + + graphAdapter.setExternalGestureListener(new CommonGraphAdapter.ExternalGestureListener() { + @Override + public void onChartGestureStart(MotionEvent me, ChartTouchListener.ChartGesture lastPerformedGesture) { + } + + @Override + public void onChartGestureEnd(MotionEvent me, ChartTouchListener.ChartGesture lastPerformedGesture, boolean hasTranslated) { + if ((lastPerformedGesture == ChartTouchListener.ChartGesture.DRAG && hasTranslated) || + lastPerformedGesture == ChartTouchListener.ChartGesture.X_ZOOM || + lastPerformedGesture == ChartTouchListener.ChartGesture.Y_ZOOM || + lastPerformedGesture == ChartTouchListener.ChartGesture.PINCH_ZOOM || + lastPerformedGesture == ChartTouchListener.ChartGesture.DOUBLE_TAP || + lastPerformedGesture == ChartTouchListener.ChartGesture.ROTATE) { + refreshChart(mapActivity, graphAdapter.getChart(), detailsMenu, true); + } + } + }); + } + + public static void refreshChart(MapActivity mapActivity, + LineChart chart, + TrackDetailsMenu menu, + boolean forceFit) { + if (mapActivity == null || chart == null || menu == null) return; + OsmandApplication app = mapActivity.getMyApplication(); + if (!app.getRoutingHelper().isFollowingMode()) { + menu.refreshChart(chart, forceFit); + mapActivity.refreshMap(); + } + } +} diff --git a/OsmAnd/src/net/osmand/plus/myplaces/TrackSegmentFragment.java b/OsmAnd/src/net/osmand/plus/myplaces/TrackSegmentFragment.java index 94b3f9bfa3..2fa80d9fa0 100644 --- a/OsmAnd/src/net/osmand/plus/myplaces/TrackSegmentFragment.java +++ b/OsmAnd/src/net/osmand/plus/myplaces/TrackSegmentFragment.java @@ -87,6 +87,9 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import static net.osmand.plus.helpers.GpxUiHelper.LineGraphType.ALTITUDE; +import static net.osmand.plus.helpers.GpxUiHelper.LineGraphType.SPEED; + public class TrackSegmentFragment extends OsmAndListFragment implements TrackBitmapDrawerListener { private OsmandApplication app; @@ -425,64 +428,16 @@ public class TrackSegmentFragment extends OsmAndListFragment implements TrackBit } } - private List getDataSets(GPXTabItemType tabType, LineChart chart) { + private List getDataSets(LineChart chart, + GPXTabItemType tabType, + GpxUiHelper.LineGraphType... types) { List dataSets = dataSetsMap.get(tabType); if (dataSets == null && chart != null) { - dataSets = new ArrayList<>(); GPXTrackAnalysis analysis = gpxItem.analysis; GpxDataItem gpxDataItem = getGpxDataItem(); boolean calcWithoutGaps = gpxItem.isGeneralTrack() && gpxDataItem != null && !gpxDataItem.isJoinSegments(); - switch (tabType) { - case GPX_TAB_ITEM_GENERAL: { - OrderedLineDataSet speedDataSet = null; - OrderedLineDataSet elevationDataSet = null; - if (analysis.hasSpeedData) { - speedDataSet = GpxUiHelper.createGPXSpeedDataSet(app, chart, - analysis, GPXDataSetAxisType.DISTANCE, true, true, calcWithoutGaps); - } - if (analysis.hasElevationData) { - elevationDataSet = GpxUiHelper.createGPXElevationDataSet(app, chart, - analysis, GPXDataSetAxisType.DISTANCE, false, true, calcWithoutGaps); - } - if (speedDataSet != null) { - dataSets.add(speedDataSet); - if (elevationDataSet != null) { - dataSets.add(elevationDataSet.getPriority() < speedDataSet.getPriority() - ? 1 : 0, elevationDataSet); - } - } else if (elevationDataSet != null) { - dataSets.add(elevationDataSet); - } - dataSetsMap.put(GPXTabItemType.GPX_TAB_ITEM_GENERAL, dataSets); - break; - } - case GPX_TAB_ITEM_ALTITUDE: { - OrderedLineDataSet elevationDataSet = GpxUiHelper.createGPXElevationDataSet(app, chart, - analysis, GPXDataSetAxisType.DISTANCE, false, true, calcWithoutGaps); - if (elevationDataSet != null) { - dataSets.add(elevationDataSet); - } - if (analysis.hasElevationData) { - List eleValues = elevationDataSet != null && !gpxItem.isGeneralTrack() ? elevationDataSet.getValues() : null; - OrderedLineDataSet slopeDataSet = GpxUiHelper.createGPXSlopeDataSet(app, chart, - analysis, GPXDataSetAxisType.DISTANCE, eleValues, true, true, calcWithoutGaps); - if (slopeDataSet != null) { - dataSets.add(slopeDataSet); - } - } - dataSetsMap.put(GPXTabItemType.GPX_TAB_ITEM_ALTITUDE, dataSets); - break; - } - case GPX_TAB_ITEM_SPEED: { - OrderedLineDataSet speedDataSet = GpxUiHelper.createGPXSpeedDataSet(app, chart, - analysis, GPXDataSetAxisType.DISTANCE, false, true, calcWithoutGaps); - if (speedDataSet != null) { - dataSets.add(speedDataSet); - } - dataSetsMap.put(GPXTabItemType.GPX_TAB_ITEM_SPEED, dataSets); - break; - } - } + dataSets = GpxUiHelper.getDataSets(chart, app, analysis, calcWithoutGaps, types); + dataSetsMap.put(tabType, dataSets); } return dataSets; } @@ -703,7 +658,7 @@ public class TrackSegmentFragment extends OsmAndListFragment implements TrackBit if (analysis != null) { if (analysis.hasElevationData || analysis.hasSpeedData) { GpxUiHelper.setupGPXChart(app, chart, 4); - chart.setData(new LineData(getDataSets(GPXTabItemType.GPX_TAB_ITEM_GENERAL, chart))); + chart.setData(new LineData(getDataSets(chart, GPXTabItemType.GPX_TAB_ITEM_GENERAL, SPEED, ALTITUDE))); updateChart(chart); chart.setVisibility(View.VISIBLE); } else { @@ -821,7 +776,7 @@ public class TrackSegmentFragment extends OsmAndListFragment implements TrackBit if (analysis != null) { if (analysis.hasElevationData) { GpxUiHelper.setupGPXChart(app, chart, 4); - chart.setData(new LineData(getDataSets(GPXTabItemType.GPX_TAB_ITEM_ALTITUDE, chart))); + chart.setData(new LineData(getDataSets(chart, GPXTabItemType.GPX_TAB_ITEM_ALTITUDE, ALTITUDE))); updateChart(chart); chart.setVisibility(View.VISIBLE); } else { @@ -923,7 +878,7 @@ public class TrackSegmentFragment extends OsmAndListFragment implements TrackBit if (analysis != null && analysis.isSpeedSpecified()) { if (analysis.hasSpeedData) { GpxUiHelper.setupGPXChart(app, chart, 4); - chart.setData(new LineData(getDataSets(GPXTabItemType.GPX_TAB_ITEM_SPEED, chart))); + chart.setData(new LineData(getDataSets(chart, GPXTabItemType.GPX_TAB_ITEM_SPEED, SPEED))); updateChart(chart); chart.setVisibility(View.VISIBLE); } else { @@ -1189,7 +1144,7 @@ public class TrackSegmentFragment extends OsmAndListFragment implements TrackBit LatLon location = null; WptPt wpt = null; gpxItem.chartTypes = null; - List ds = getDataSets(tabType, null); + List ds = getDataSets(null, tabType); if (ds != null && ds.size() > 0) { gpxItem.chartTypes = new GPXDataSetType[ds.size()]; for (int i = 0; i < ds.size(); i++) { diff --git a/OsmAnd/src/net/osmand/plus/routepreparationmenu/RouteDetailsFragment.java b/OsmAnd/src/net/osmand/plus/routepreparationmenu/RouteDetailsFragment.java index 1f29dfd1b7..81c3f232d8 100644 --- a/OsmAnd/src/net/osmand/plus/routepreparationmenu/RouteDetailsFragment.java +++ b/OsmAnd/src/net/osmand/plus/routepreparationmenu/RouteDetailsFragment.java @@ -2,7 +2,6 @@ package net.osmand.plus.routepreparationmenu; import android.annotation.SuppressLint; import android.content.Context; -import android.graphics.RectF; import android.graphics.Typeface; import android.graphics.drawable.Drawable; import android.graphics.drawable.GradientDrawable; @@ -14,11 +13,9 @@ import android.text.TextUtils; import android.text.style.ForegroundColorSpan; import android.view.Gravity; import android.view.LayoutInflater; -import android.view.MotionEvent; import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnLongClickListener; -import android.view.View.OnTouchListener; import android.view.ViewGroup; import android.widget.FrameLayout; import android.widget.ImageView; @@ -30,15 +27,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.content.ContextCompat; -import com.github.mikephil.charting.animation.ChartAnimator; -import com.github.mikephil.charting.charts.HorizontalBarChart; import com.github.mikephil.charting.charts.LineChart; -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.highlight.Highlight; -import com.github.mikephil.charting.interfaces.dataprovider.BarDataProvider; -import com.github.mikephil.charting.listener.ChartTouchListener.ChartGesture; -import com.github.mikephil.charting.renderer.HorizontalBarChartRenderer; -import com.github.mikephil.charting.utils.ViewPortHandler; import net.osmand.AndroidUtils; import net.osmand.GPXUtilities.GPXFile; @@ -68,10 +57,12 @@ import net.osmand.plus.helpers.GpxUiHelper.GPXDataSetType; import net.osmand.plus.helpers.GpxUiHelper.OrderedLineDataSet; import net.osmand.plus.mapcontextmenu.CollapsableView; import net.osmand.plus.mapcontextmenu.other.TrackDetailsMenu; +import net.osmand.plus.measurementtool.graph.BaseGraphAdapter; +import net.osmand.plus.measurementtool.graph.CommonGraphAdapter; +import net.osmand.plus.measurementtool.graph.GraphAdapterHelper; import net.osmand.plus.render.MapRenderRepositories; import net.osmand.plus.routepreparationmenu.cards.BaseCard; import net.osmand.plus.routepreparationmenu.cards.BaseCard.CardListener; -import net.osmand.plus.routepreparationmenu.cards.CardChartListener; import net.osmand.plus.routepreparationmenu.cards.PublicTransportCard; import net.osmand.plus.routepreparationmenu.cards.PublicTransportCard.PublicTransportCardListener; import net.osmand.plus.routepreparationmenu.cards.RouteDirectionsCard; @@ -98,8 +89,8 @@ import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; -public class RouteDetailsFragment extends ContextMenuFragment implements PublicTransportCardListener, - CardListener, CardChartListener { +public class RouteDetailsFragment extends ContextMenuFragment + implements PublicTransportCardListener, CardListener { public static final String ROUTE_ID_KEY = "route_id_key"; private static final float PAGE_MARGIN = 5f; @@ -311,24 +302,7 @@ public class RouteDetailsFragment extends ContextMenuFragment implements PublicT return; } OsmandApplication app = mapActivity.getMyApplication(); - statisticCard = new RouteStatisticCard(mapActivity, gpx, new OnTouchListener() { - @Override - public boolean onTouch(View v, MotionEvent ev) { - LinearLayout mainView = getMainView(); - if (mainView != null) { - mainView.requestDisallowInterceptTouchEvent(true); - } - for (RouteInfoCard card : routeInfoCards) { - final HorizontalBarChart ch = card.getChart(); - if (ch != null) { - final MotionEvent event = MotionEvent.obtainNoHistory(ev); - event.setSource(0); - ch.dispatchTouchEvent(event); - } - } - return false; - } - }, new OnClickListener() { + statisticCard = new RouteStatisticCard(mapActivity, gpx, new OnClickListener() { @Override public void onClick(View v) { openDetails(); @@ -336,7 +310,6 @@ public class RouteDetailsFragment extends ContextMenuFragment implements PublicT }); statisticCard.setTransparentBackground(true); statisticCard.setListener(this); - statisticCard.setChartListener(this); menuCards.add(statisticCard); cardsContainer.addView(statisticCard.build(mapActivity)); buildRowDivider(cardsContainer, false); @@ -359,13 +332,25 @@ public class RouteDetailsFragment extends ContextMenuFragment implements PublicT routeDetailsMenu.setGpxItem(gpxItem); } routeDetailsMenu.setMapActivity(mapActivity); - LineChart chart = statisticCard.getChart(); - if (chart != null) { - chart.setExtraRightOffset(16); - chart.setExtraLeftOffset(16); + + CommonGraphAdapter mainGraphAdapter = statisticCard.getGraphAdapter(); + if (mainGraphAdapter != null) { + GraphAdapterHelper.bindGraphAdapters(mainGraphAdapter, getRouteInfoCardsGraphAdapters(), getMainView()); + GraphAdapterHelper.bindToMap(mainGraphAdapter, mapActivity, routeDetailsMenu); } } + private List getRouteInfoCardsGraphAdapters() { + List adapters = new ArrayList<>(); + for (RouteInfoCard card : routeInfoCards) { + BaseGraphAdapter adapter = card.getGraphAdapter(); + if (adapter != null) { + adapters.add(adapter); + } + } + return adapters; + } + public static List calculateRouteStatistics(OsmandApplication app, List route, boolean nightMode) { @@ -384,7 +369,12 @@ public class RouteDetailsFragment extends ContextMenuFragment implements PublicT protected void calculateLayout(View view, boolean initLayout) { super.calculateLayout(view, initLayout); if (!initLayout && getCurrentMenuState() != MenuState.FULL_SCREEN) { - refreshChart(false); + MapActivity mapActivity = getMapActivity(); + CommonGraphAdapter mainGraphAdapter = statisticCard.getGraphAdapter(); + if (mainGraphAdapter != null) { + LineChart chart = mainGraphAdapter.getChart(); + GraphAdapterHelper.refreshChart(mapActivity, chart, routeDetailsMenu, false); + } } } @@ -401,48 +391,15 @@ public class RouteDetailsFragment extends ContextMenuFragment implements PublicT buildRowDivider(cardsContainer, false); } - private OnTouchListener getChartTouchListener() { - return new OnTouchListener() { - @SuppressLint("ClickableViewAccessibility") - @Override - public boolean onTouch(View v, MotionEvent ev) { - if (ev.getSource() != 0 && v instanceof HorizontalBarChart) { - if (statisticCard != null) { - LineChart ch = statisticCard.getChart(); - if (ch != null) { - final MotionEvent event = MotionEvent.obtainNoHistory(ev); - event.setSource(0); - ch.dispatchTouchEvent(event); - } - } - return true; - } - return false; - } - }; - } - @SuppressLint("ClickableViewAccessibility") - private void addRouteCard(final LinearLayout cardsContainer, RouteInfoCard routeInfoCard) { + private void addRouteCard(LinearLayout cardsContainer, + RouteInfoCard routeInfoCard) { OsmandApplication app = requireMyApplication(); menuCards.add(routeInfoCard); routeInfoCard.setListener(this); cardsContainer.addView(routeInfoCard.build(app)); buildRowDivider(cardsContainer, false); - routeInfoCards.add(routeInfoCard); - HorizontalBarChart chart = routeInfoCard.getChart(); - if (chart != null) { - LineChart mainChart = statisticCard.getChart(); - if (mainChart != null) { - chart.getAxisRight().setAxisMinimum(mainChart.getXChartMin()); - chart.getAxisRight().setAxisMaximum(mainChart.getXChartMax()); - } - chart.setRenderer(new CustomBarChartRenderer(chart, chart.getAnimator(), chart.getViewPortHandler(), AndroidUtils.dpToPx(app, 1f) / 2f)); - chart.setHighlightPerDragEnabled(false); - chart.setHighlightPerTapEnabled(false); - chart.setOnTouchListener(getChartTouchListener()); - } } public Drawable getCollapseIcon(boolean collapsed) { @@ -1615,59 +1572,6 @@ public class RouteDetailsFragment extends ContextMenuFragment implements PublicT } } - private void refreshChart(boolean forceFit) { - MapActivity mapActivity = getMapActivity(); - if (mapActivity != null && routeDetailsMenu != null && statisticCard != null && - !mapActivity.getMyApplication().getRoutingHelper().isFollowingMode()) { - LineChart chart = statisticCard.getChart(); - if (chart != null) { - routeDetailsMenu.refreshChart(chart, forceFit); - mapActivity.refreshMap(); - } - } - } - - private void highlightRouteInfoCharts(@Nullable Highlight h) { - for (RouteInfoCard rc : routeInfoCards) { - HorizontalBarChart chart = rc.getChart(); - if (chart != null) { - Highlight bh = h != null ? chart.getHighlighter().getHighlight(1, h.getXPx()) : null; - if (bh != null) { - bh.setDraw(h.getXPx(), 0); - } - chart.highlightValue(bh, true); - } - } - } - - @Override - public void onValueSelected(BaseCard card, Entry e, Highlight h) { - refreshChart(false); - highlightRouteInfoCharts(h); - } - - @Override - public void onNothingSelected(BaseCard card) { - highlightRouteInfoCharts(null); - } - - @Override - public void onChartGestureStart(BaseCard card, MotionEvent me, ChartGesture lastPerformedGesture) { - } - - @Override - public void onChartGestureEnd(BaseCard card, MotionEvent me, ChartGesture lastPerformedGesture, boolean hasTranslated) { - if ((lastPerformedGesture == ChartGesture.DRAG && hasTranslated) || - lastPerformedGesture == ChartGesture.X_ZOOM || - lastPerformedGesture == ChartGesture.Y_ZOOM || - lastPerformedGesture == ChartGesture.PINCH_ZOOM || - lastPerformedGesture == ChartGesture.DOUBLE_TAP || - lastPerformedGesture == ChartGesture.ROTATE) { - - refreshChart(true); - } - } - public static class CumulativeInfo { public int distance; public int time; @@ -1696,20 +1600,4 @@ public class RouteDetailsFragment extends ContextMenuFragment implements PublicT final int timeInSeconds = model.getExpectedTime(); return Algorithms.formatDuration(timeInSeconds, app.accessibilityEnabled()); } - - private static class CustomBarChartRenderer extends HorizontalBarChartRenderer { - - private float highlightHalfWidth; - - CustomBarChartRenderer(BarDataProvider chart, ChartAnimator animator, ViewPortHandler viewPortHandler, float highlightHalfWidth) { - super(chart, animator, viewPortHandler); - this.highlightHalfWidth = highlightHalfWidth; - } - - @Override - protected void setHighlightDrawPos(Highlight high, RectF bar) { - bar.left = high.getDrawX() - highlightHalfWidth; - bar.right = high.getDrawX() + highlightHalfWidth; - } - } } \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/routepreparationmenu/cards/BaseCard.java b/OsmAnd/src/net/osmand/plus/routepreparationmenu/cards/BaseCard.java index 245412fe34..d4101e58ec 100644 --- a/OsmAnd/src/net/osmand/plus/routepreparationmenu/cards/BaseCard.java +++ b/OsmAnd/src/net/osmand/plus/routepreparationmenu/cards/BaseCard.java @@ -31,7 +31,6 @@ public abstract class BaseCard { protected boolean nightMode; private CardListener listener; - private CardChartListener chartListener; public interface CardListener { void onCardLayoutNeeded(@NonNull BaseCard card); @@ -78,14 +77,6 @@ public abstract class BaseCard { this.listener = listener; } - public CardChartListener getChartListener() { - return chartListener; - } - - public void setChartListener(CardChartListener chartListener) { - this.chartListener = chartListener; - } - public void setLayoutNeeded() { CardListener listener = this.listener; if (listener != null) { diff --git a/OsmAnd/src/net/osmand/plus/routepreparationmenu/cards/RouteInfoCard.java b/OsmAnd/src/net/osmand/plus/routepreparationmenu/cards/RouteInfoCard.java index ef02b16734..d50f0dfdc8 100644 --- a/OsmAnd/src/net/osmand/plus/routepreparationmenu/cards/RouteInfoCard.java +++ b/OsmAnd/src/net/osmand/plus/routepreparationmenu/cards/RouteInfoCard.java @@ -1,55 +1,33 @@ package net.osmand.plus.routepreparationmenu.cards; import android.graphics.drawable.Drawable; -import android.text.Spannable; -import android.text.SpannableStringBuilder; -import android.text.style.StyleSpan; -import android.view.LayoutInflater; import android.view.View; -import android.view.ViewGroup; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; -import androidx.annotation.Nullable; -import androidx.appcompat.view.ContextThemeWrapper; -import androidx.core.content.ContextCompat; -import androidx.core.graphics.ColorUtils; - import com.github.mikephil.charting.charts.HorizontalBarChart; import com.github.mikephil.charting.data.BarData; -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.highlight.Highlight; -import com.github.mikephil.charting.listener.OnChartValueSelectedListener; import net.osmand.GPXUtilities.GPXTrackAnalysis; -import net.osmand.plus.OsmAndFormatter; import net.osmand.plus.R; import net.osmand.plus.activities.MapActivity; import net.osmand.plus.activities.SettingsBaseActivity; -import net.osmand.plus.activities.SettingsNavigationActivity; import net.osmand.plus.helpers.GpxUiHelper; -import net.osmand.router.RouteStatisticsHelper.RouteSegmentAttribute; +import net.osmand.plus.measurementtool.graph.CustomGraphAdapter; +import net.osmand.plus.measurementtool.graph.CustomGraphAdapter.LegendViewType; import net.osmand.router.RouteStatisticsHelper.RouteStatistics; -import net.osmand.util.Algorithms; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; public class RouteInfoCard extends BaseCard { - - private static final int MINIMUM_CONTRAST_RATIO = 3; - - private RouteStatistics routeStatistics; + private RouteStatistics statistics; private GPXTrackAnalysis analysis; - private String selectedPropertyName; + private CustomGraphAdapter graphAdapter; private boolean showLegend; - public RouteInfoCard(MapActivity mapActivity, RouteStatistics routeStatistics, GPXTrackAnalysis analysis) { + public RouteInfoCard(MapActivity mapActivity, RouteStatistics statistics, GPXTrackAnalysis analysis) { super(mapActivity); - this.routeStatistics = routeStatistics; + this.statistics = statistics; this.analysis = analysis; } @@ -60,112 +38,46 @@ public class RouteInfoCard extends BaseCard { @Override protected void updateContent() { - updateContent(routeStatistics); - } - - @Nullable - public HorizontalBarChart getChart() { - return (HorizontalBarChart) view.findViewById(R.id.chart); - } - - private void updateContent(final RouteStatistics routeStatistics) { updateHeader(); - final HorizontalBarChart chart = (HorizontalBarChart) view.findViewById(R.id.chart); - GpxUiHelper.setupHorizontalGPXChart(app, chart, 5, 9, 24, true, nightMode); - chart.setExtraRightOffset(16); - chart.setExtraLeftOffset(16); - BarData barData = GpxUiHelper.buildStatisticChart(app, chart, routeStatistics, analysis, true, nightMode); - chart.setData(barData); - chart.setOnChartValueSelectedListener(new OnChartValueSelectedListener() { - @Override - public void onValueSelected(Entry e, Highlight h) { - List elems = routeStatistics.elements; - int i = h.getStackIndex(); - if (i >= 0 && elems.size() > i) { - selectedPropertyName = elems.get(i).getPropertyName(); - if (showLegend) { - updateLegend(routeStatistics); - } - } - } - - @Override - public void onNothingSelected() { - selectedPropertyName = null; - if (showLegend) { - updateLegend(routeStatistics); - } - } - }); LinearLayout container = (LinearLayout) view.findViewById(R.id.route_items); - container.removeAllViews(); - if (showLegend) { - attachLegend(container, routeStatistics); - } - final ImageView iconViewCollapse = (ImageView) view.findViewById(R.id.up_down_icon); - iconViewCollapse.setImageDrawable(getCollapseIcon(!showLegend)); + HorizontalBarChart chart = (HorizontalBarChart) view.findViewById(R.id.chart); + BarData barData = GpxUiHelper.buildStatisticChart(app, chart, statistics, analysis, true, nightMode); + graphAdapter = new CustomGraphAdapter(chart, true); + graphAdapter.setLegendContainer(container); + graphAdapter.updateData(barData, statistics); + updateView(); + view.findViewById(R.id.info_type_details_button).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { showLegend = !showLegend; - updateContent(); + updateView(); setLayoutNeeded(); } }); } - protected void updateLegend(RouteStatistics routeStatistics) { - LinearLayout container = (LinearLayout) view.findViewById(R.id.route_items); - container.removeAllViews(); - attachLegend(container, routeStatistics); - setLayoutNeeded(); - } - - private Drawable getCollapseIcon(boolean collapsed) { - return collapsed ? getContentIcon(R.drawable.ic_action_arrow_down) : getActiveIcon(R.drawable.ic_action_arrow_up); + private void updateView() { + updateCollapseIcon(); + graphAdapter.setLegendViewType(showLegend ? LegendViewType.ALL_AS_LIST : LegendViewType.GONE); + graphAdapter.updateView(); } private void updateHeader() { TextView title = (TextView) view.findViewById(R.id.info_type_title); - String name = SettingsBaseActivity.getStringRouteInfoPropertyValue(app, routeStatistics.name); + String name = SettingsBaseActivity.getStringRouteInfoPropertyValue(app, statistics.name); title.setText(name); } - private void attachLegend(ViewGroup container, RouteStatistics routeStatistics) { - Map partition = routeStatistics.partition; - List> list = new ArrayList<>(partition.entrySet()); - ContextThemeWrapper ctx = new ContextThemeWrapper(mapActivity, !nightMode ? R.style.OsmandLightTheme : R.style.OsmandDarkTheme); - LayoutInflater inflater = LayoutInflater.from(ctx); - for (Map.Entry entry : list) { - RouteSegmentAttribute segment = entry.getValue(); - View view = inflater.inflate(R.layout.route_details_legend, container, false); - int segmentColor = segment.getColor(); - Drawable circle = app.getUIUtilities().getPaintedIcon(R.drawable.ic_action_circle, segmentColor); - ImageView legendIcon = (ImageView) view.findViewById(R.id.legend_icon_color); - legendIcon.setImageDrawable(circle); - double contrastRatio = ColorUtils.calculateContrast(segmentColor, ContextCompat.getColor(app, nightMode ? R.color.card_and_list_background_dark : R.color.card_and_list_background_light)); - if (contrastRatio < MINIMUM_CONTRAST_RATIO) { - legendIcon.setBackgroundResource(nightMode ? R.drawable.circle_contour_bg_dark : R.drawable.circle_contour_bg_light); - } - String propertyName = segment.getUserPropertyName(); - String name = SettingsNavigationActivity.getStringPropertyName(app, propertyName, propertyName.replaceAll("_", " ")); - Spannable text = getSpanLegend(name, segment, segment.getUserPropertyName().equals(selectedPropertyName)); - TextView legend = (TextView) view.findViewById(R.id.legend_text); - legend.setText(text); - - container.addView(view); - } + private void updateCollapseIcon() { + ImageView ivCollapse = (ImageView) view.findViewById(R.id.up_down_icon); + Drawable drawable = showLegend ? + getContentIcon(R.drawable.ic_action_arrow_down) : + getActiveIcon(R.drawable.ic_action_arrow_up); + ivCollapse.setImageDrawable(drawable); } - private Spannable getSpanLegend(String title, RouteSegmentAttribute segment, boolean selected) { - String formattedDistance = OsmAndFormatter.getFormattedDistance(segment.getDistance(), getMyApplication()); - title = Algorithms.capitalizeFirstLetter(title); - SpannableStringBuilder spannable = new SpannableStringBuilder(title); - spannable.append(": "); - int startIndex = selected ? -0 : spannable.length(); - spannable.append(formattedDistance); - spannable.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), startIndex, spannable.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); - - return spannable; + public CustomGraphAdapter getGraphAdapter() { + return graphAdapter; } } \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/routepreparationmenu/cards/RouteStatisticCard.java b/OsmAnd/src/net/osmand/plus/routepreparationmenu/cards/RouteStatisticCard.java index ef37797f0a..bb6483155b 100644 --- a/OsmAnd/src/net/osmand/plus/routepreparationmenu/cards/RouteStatisticCard.java +++ b/OsmAnd/src/net/osmand/plus/routepreparationmenu/cards/RouteStatisticCard.java @@ -1,13 +1,10 @@ package net.osmand.plus.routepreparationmenu.cards; -import android.graphics.Matrix; import android.os.Build; import android.text.SpannableStringBuilder; import android.text.style.ForegroundColorSpan; -import android.view.MotionEvent; import android.view.View; import android.view.View.OnClickListener; -import android.view.View.OnTouchListener; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.TextView; @@ -16,18 +13,12 @@ import androidx.annotation.Nullable; import androidx.core.content.ContextCompat; import com.github.mikephil.charting.charts.LineChart; -import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.LineData; -import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; -import com.github.mikephil.charting.listener.ChartTouchListener.ChartGesture; -import com.github.mikephil.charting.listener.OnChartGestureListener; -import com.github.mikephil.charting.listener.OnChartValueSelectedListener; import net.osmand.AndroidUtils; import net.osmand.GPXUtilities.GPXFile; import net.osmand.GPXUtilities.GPXTrackAnalysis; -import net.osmand.plus.GpxSelectionHelper; import net.osmand.plus.GpxSelectionHelper.GpxDisplayItem; import net.osmand.plus.OsmAndFormatter; import net.osmand.plus.OsmandApplication; @@ -36,6 +27,7 @@ import net.osmand.plus.activities.MapActivity; import net.osmand.plus.helpers.GpxUiHelper; import net.osmand.plus.helpers.GpxUiHelper.GPXDataSetAxisType; import net.osmand.plus.helpers.GpxUiHelper.OrderedLineDataSet; +import net.osmand.plus.measurementtool.graph.CommonGraphAdapter; import net.osmand.plus.routing.RoutingHelper; import java.util.ArrayList; @@ -52,16 +44,15 @@ public class RouteStatisticCard extends BaseCard { private OrderedLineDataSet slopeDataSet; @Nullable private OrderedLineDataSet elevationDataSet; - private OnTouchListener onTouchListener; private OnClickListener onAnalyseClickListener; + private CommonGraphAdapter graphAdapter; - public RouteStatisticCard(MapActivity mapActivity, GPXFile gpx, OnTouchListener onTouchListener, + public RouteStatisticCard(MapActivity mapActivity, GPXFile gpx, OnClickListener onAnalyseClickListener) { super(mapActivity); this.gpx = gpx; - this.onTouchListener = onTouchListener; this.onAnalyseClickListener = onAnalyseClickListener; - makeGpxDisplayItem(); + this.gpxItem = GpxUiHelper.makeGpxDisplayItem(app, gpx); } @Nullable @@ -219,26 +210,14 @@ public class RouteStatisticCard extends BaseCard { return elevationDataSet; } - private void makeGpxDisplayItem() { - String groupName = getMyApplication().getString(R.string.current_route); - GpxSelectionHelper.GpxDisplayGroup group = getMyApplication().getSelectedGpxHelper().buildGpxDisplayGroup(gpx, 0, groupName); - if (group != null && group.getModifiableList().size() > 0) { - gpxItem = group.getModifiableList().get(0); - if (gpxItem != null) { - gpxItem.route = true; - } - } - } - @Nullable - public LineChart getChart() { - return (LineChart) view.findViewById(R.id.chart); + public CommonGraphAdapter getGraphAdapter() { + return graphAdapter; } private void buildHeader(GPXTrackAnalysis analysis) { - final LineChart mChart = (LineChart) view.findViewById(R.id.chart); - GpxUiHelper.setupGPXChart(mChart, 4, 24f, 16f, !nightMode, true); - mChart.setOnTouchListener(onTouchListener); + LineChart mChart = (LineChart) view.findViewById(R.id.chart); + graphAdapter = new CommonGraphAdapter(mChart, true); if (analysis.hasElevationData) { List dataSets = new ArrayList<>(); @@ -256,99 +235,7 @@ public class RouteStatisticCard extends BaseCard { this.elevationDataSet = elevationDataSet; this.slopeDataSet = slopeDataSet; - LineData data = new LineData(dataSets); - mChart.setData(data); - - mChart.setOnChartValueSelectedListener(new OnChartValueSelectedListener() { - @Override - public void onValueSelected(Entry e, Highlight h) { - CardChartListener chartListener = getChartListener(); - if (chartListener != null) { - chartListener.onValueSelected(RouteStatisticCard.this, e, h); - } - } - - @Override - public void onNothingSelected() { - CardChartListener chartListener = getChartListener(); - if (chartListener != null) { - chartListener.onNothingSelected(RouteStatisticCard.this); - } - } - }); - - mChart.setOnChartGestureListener(new OnChartGestureListener() { - boolean hasTranslated = false; - float highlightDrawX = -1; - - @Override - public void onChartGestureStart(MotionEvent me, ChartGesture lastPerformedGesture) { - hasTranslated = false; - if (mChart.getHighlighted() != null && mChart.getHighlighted().length > 0) { - highlightDrawX = mChart.getHighlighted()[0].getDrawX(); - } else { - highlightDrawX = -1; - } - CardChartListener chartListener = getChartListener(); - if (chartListener != null) { - chartListener.onChartGestureStart(RouteStatisticCard.this, me, lastPerformedGesture); - } - } - - @Override - public void onChartGestureEnd(MotionEvent me, ChartGesture lastPerformedGesture) { - gpxItem.chartMatrix = new Matrix(mChart.getViewPortHandler().getMatrixTouch()); - Highlight[] highlights = mChart.getHighlighted(); - if (highlights != null && highlights.length > 0) { - gpxItem.chartHighlightPos = highlights[0].getX(); - } else { - gpxItem.chartHighlightPos = -1; - } - CardChartListener chartListener = getChartListener(); - if (chartListener != null) { - chartListener.onChartGestureEnd(RouteStatisticCard.this, me, lastPerformedGesture, hasTranslated); - } - } - - @Override - public void onChartLongPressed(MotionEvent me) { - } - - @Override - public void onChartDoubleTapped(MotionEvent me) { - } - - @Override - public void onChartSingleTapped(MotionEvent me) { - } - - @Override - public void onChartFling(MotionEvent me1, MotionEvent me2, float velocityX, float velocityY) { - } - - @Override - public void onChartScale(MotionEvent me, float scaleX, float scaleY) { - } - - @Override - public void onChartTranslate(MotionEvent me, float dX, float dY) { - hasTranslated = true; - if (highlightDrawX != -1) { - Highlight h = mChart.getHighlightByTouchPoint(highlightDrawX, 0f); - if (h != null) { - /* - ILineDataSet set = mChart.getLineData().getDataSetByIndex(h.getDataSetIndex()); - if (set != null && set.isHighlightEnabled()) { - Entry e = set.getEntryForXValue(h.getX(), h.getY()); - MPPointD pix = mChart.getTransformer(set.getAxisDependency()).getPixelForValues(e.getX(), e.getY()); - h.setDraw((float) pix.x, (float) pix.y); - } - */ - mChart.highlightValue(h, true); - } - } - } - }); + graphAdapter.fullUpdate(new LineData(dataSets), gpxItem); mChart.setVisibility(View.VISIBLE); } else { mChart.setVisibility(View.GONE); From 4e80496d1e7046d0886e57954994069ba0c3475f Mon Sep 17 00:00:00 2001 From: nazar-kutz Date: Sun, 1 Nov 2020 16:42:54 +0200 Subject: [PATCH 3/8] remove unnecessary import --- OsmAnd/src/net/osmand/plus/measurementtool/GraphsCard.java | 1 - 1 file changed, 1 deletion(-) diff --git a/OsmAnd/src/net/osmand/plus/measurementtool/GraphsCard.java b/OsmAnd/src/net/osmand/plus/measurementtool/GraphsCard.java index 3ae28d83b2..36c8e797f1 100644 --- a/OsmAnd/src/net/osmand/plus/measurementtool/GraphsCard.java +++ b/OsmAnd/src/net/osmand/plus/measurementtool/GraphsCard.java @@ -36,7 +36,6 @@ import net.osmand.plus.measurementtool.graph.BaseGraphAdapter; import net.osmand.plus.measurementtool.graph.GraphAdapterHelper; import net.osmand.plus.routepreparationmenu.RouteDetailsFragment; import net.osmand.plus.routepreparationmenu.cards.BaseCard; -import net.osmand.plus.routepreparationmenu.cards.RouteStatisticCard; import net.osmand.router.RouteSegmentResult; import net.osmand.util.Algorithms; From ca6e5413d1e5418c72e321da42eb750e643f3078 Mon Sep 17 00:00:00 2001 From: nazar-kutz Date: Mon, 2 Nov 2020 13:52:16 +0200 Subject: [PATCH 4/8] small fixes after SettingsBaseActivity removed --- OsmAnd/res/values/strings.xml | 1 + .../plus/measurementtool/graph/CustomGraphAdapter.java | 7 +++---- .../plus/routepreparationmenu/cards/RouteInfoCard.java | 3 +-- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/OsmAnd/res/values/strings.xml b/OsmAnd/res/values/strings.xml index 7861393b31..91eec18222 100644 --- a/OsmAnd/res/values/strings.xml +++ b/OsmAnd/res/values/strings.xml @@ -11,6 +11,7 @@ Thx - Hardy --> + You must add at least two points. Travel Emergency Sport diff --git a/OsmAnd/src/net/osmand/plus/measurementtool/graph/CustomGraphAdapter.java b/OsmAnd/src/net/osmand/plus/measurementtool/graph/CustomGraphAdapter.java index aded2e0fbe..e62047c151 100644 --- a/OsmAnd/src/net/osmand/plus/measurementtool/graph/CustomGraphAdapter.java +++ b/OsmAnd/src/net/osmand/plus/measurementtool/graph/CustomGraphAdapter.java @@ -22,7 +22,6 @@ import net.osmand.AndroidUtils; import net.osmand.plus.OsmAndFormatter; import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; -import net.osmand.plus.activities.SettingsNavigationActivity; import net.osmand.plus.helpers.CustomBarChartRenderer; import net.osmand.plus.helpers.GpxUiHelper; import net.osmand.router.RouteStatisticsHelper; @@ -34,9 +33,9 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; -public class CustomGraphAdapter extends BaseGraphAdapter { +import static net.osmand.plus.track.ColorsCard.MINIMUM_CONTRAST_RATIO; - private static final int MINIMUM_CONTRAST_RATIO = 3; +public class CustomGraphAdapter extends BaseGraphAdapter { private String selectedPropertyName; private ViewGroup legendContainer; @@ -153,7 +152,7 @@ public class CustomGraphAdapter extends BaseGraphAdapter Date: Tue, 3 Nov 2020 11:30:53 +0200 Subject: [PATCH 5/8] fix "Plan Route: Graphs" bugs and refactoring --- .../fragment_measurement_tool_graph.xml | 122 ++++++++------ .../net/osmand/plus/helpers/GpxUiHelper.java | 108 ++++++++---- .../plus/measurementtool/GraphsCard.java | 157 ++++++++++-------- .../graph/BaseGraphAdapter.java | 34 ++-- .../graph/CommonGraphAdapter.java | 40 ++--- .../graph/CustomGraphAdapter.java | 31 ++-- .../plus/myplaces/TrackSegmentFragment.java | 5 +- .../RouteDetailsFragment.java | 9 +- .../cards/RouteInfoCard.java | 1 + .../cards/RouteStatisticCard.java | 3 +- 10 files changed, 289 insertions(+), 221 deletions(-) diff --git a/OsmAnd/res/layout/fragment_measurement_tool_graph.xml b/OsmAnd/res/layout/fragment_measurement_tool_graph.xml index 45f0b484d5..555c1b6928 100644 --- a/OsmAnd/res/layout/fragment_measurement_tool_graph.xml +++ b/OsmAnd/res/layout/fragment_measurement_tool_graph.xml @@ -26,9 +26,9 @@ android:id="@+id/common_graphs_container" android:layout_width="match_parent" android:layout_height="wrap_content" + android:orientation="vertical" android:visibility="gone" - tools:visibility="visible" - android:orientation="vertical"> + tools:visibility="visible"> + tools:visibility="visible"> + android:background="?attr/list_divider" /> - - - - + tools:visibility="visible"> + android:layout_marginStart="@dimen/content_padding" + android:layout_marginLeft="@dimen/content_padding" + android:layout_marginTop="@dimen/content_padding_small" + android:layout_marginEnd="@dimen/content_padding" + android:layout_marginRight="@dimen/content_padding" + android:orientation="horizontal"> - + + + + + + + + + + + + + + + @@ -134,8 +158,8 @@ android:id="@+id/btn_text" android:layout_width="match_parent" android:layout_height="0dp" - android:layout_weight="1" android:layout_gravity="start|center_vertical" + android:layout_weight="1" android:gravity="start|center_vertical" android:lineSpacingMultiplier="@dimen/bottom_sheet_text_spacing_multiplier" android:textColor="@color/preference_category_title" diff --git a/OsmAnd/src/net/osmand/plus/helpers/GpxUiHelper.java b/OsmAnd/src/net/osmand/plus/helpers/GpxUiHelper.java index edcf3dc07f..dd2a35a9a4 100644 --- a/OsmAnd/src/net/osmand/plus/helpers/GpxUiHelper.java +++ b/OsmAnd/src/net/osmand/plus/helpers/GpxUiHelper.java @@ -2057,47 +2057,85 @@ public class GpxUiHelper { OsmandApplication app, GPXTrackAnalysis analysis, boolean calcWithoutGaps, - LineGraphType... types) { + LineGraphType ... types) { if (app == null || chart == null || analysis == null || types == null) { - return Collections.emptyList(); + return new ArrayList<>(); } - List dataList = new ArrayList<>(); - for (LineGraphType type : types) { - switch (type) { - case ALTITUDE: { - if (analysis.hasElevationData) { - OrderedLineDataSet elevationDataSet = GpxUiHelper.createGPXElevationDataSet(app, chart, - analysis, GPXDataSetAxisType.DISTANCE, false, true, calcWithoutGaps); - dataList.add(elevationDataSet); - } - break; + if (types.length > 1) { + return getDataSetsImpl(chart, app, analysis, calcWithoutGaps, types[0], types[1]); + } else { + return getDataSetsImpl(chart, app, analysis, calcWithoutGaps, types[0]); + } + } + + private static List getDataSetsImpl(@NonNull LineChart chart, + @NonNull OsmandApplication app, + @NonNull GPXTrackAnalysis analysis, + boolean calcWithoutGaps, + @NonNull LineGraphType type) { + List result = new ArrayList<>(); + ILineDataSet dataSet = getDataSet(chart, app, analysis, calcWithoutGaps, false, type); + if (dataSet != null) { + result.add(dataSet); + } + return result; + } + + private static List getDataSetsImpl(@NonNull LineChart chart, + @NonNull OsmandApplication app, + @NonNull GPXTrackAnalysis analysis, + boolean calcWithoutGaps, + @NonNull LineGraphType type1, + @NonNull LineGraphType type2) { + List result = new ArrayList<>(); + OrderedLineDataSet dataSet1 = getDataSet(chart, app, analysis, calcWithoutGaps, false, type1); + OrderedLineDataSet dataSet2 = getDataSet(chart, app, analysis, calcWithoutGaps, true, type2); + if (dataSet1 == null && dataSet2 == null) { + return new ArrayList<>(); + } else if (dataSet1 == null) { + result.add(dataSet2); + } else if (dataSet2 == null) { + result.add(dataSet1); + } else if (dataSet1.getPriority() < dataSet2.getPriority()) { + result.add(dataSet2); + result.add(dataSet1); + } else { + result.add(dataSet1); + result.add(dataSet2); + } + return result; + } + + private static OrderedLineDataSet getDataSet(@NonNull LineChart chart, + @NonNull OsmandApplication app, + @NonNull GPXTrackAnalysis analysis, + boolean calcWithoutGaps, + boolean useRightAxis, + @NonNull LineGraphType type) { + OrderedLineDataSet dataSet = null; + switch (type) { + case ALTITUDE: { + if (analysis.hasElevationData) { + dataSet = GpxUiHelper.createGPXElevationDataSet(app, chart, + analysis, GPXDataSetAxisType.DISTANCE, useRightAxis, true, calcWithoutGaps); } - case SLOPE: - if (analysis.hasElevationData) { - OrderedLineDataSet slopeDataSet = GpxUiHelper.createGPXSlopeDataSet(app, chart, - analysis, GPXDataSetAxisType.DISTANCE, null, true, true, calcWithoutGaps); - dataList.add(slopeDataSet); - } - break; - case SPEED: { - if (analysis.hasSpeedData) { - OrderedLineDataSet speedDataSet = GpxUiHelper.createGPXSpeedDataSet(app, chart, - analysis, GPXDataSetAxisType.DISTANCE, false, true, calcWithoutGaps); - dataList.add(speedDataSet); - } - break; + break; + } + case SLOPE: + if (analysis.hasElevationData) { + dataSet = GpxUiHelper.createGPXSlopeDataSet(app, chart, + analysis, GPXDataSetAxisType.DISTANCE, null, useRightAxis, true, calcWithoutGaps); } + break; + case SPEED: { + if (analysis.hasSpeedData) { + dataSet = GpxUiHelper.createGPXSpeedDataSet(app, chart, + analysis, GPXDataSetAxisType.DISTANCE, useRightAxis, true, calcWithoutGaps); + } + break; } } - if (dataList.size() > 0) { - Collections.sort(dataList, new Comparator() { - @Override - public int compare(OrderedLineDataSet o1, OrderedLineDataSet o2) { - return Float.compare(o1.getPriority(), o2.getPriority()); - } - }); - } - return new ArrayList(dataList); + return dataSet; } public static GpxDisplayItem makeGpxDisplayItem(OsmandApplication app, GPXUtilities.GPXFile gpx) { diff --git a/OsmAnd/src/net/osmand/plus/measurementtool/GraphsCard.java b/OsmAnd/src/net/osmand/plus/measurementtool/GraphsCard.java index a7f35b4d0a..69277f19a6 100644 --- a/OsmAnd/src/net/osmand/plus/measurementtool/GraphsCard.java +++ b/OsmAnd/src/net/osmand/plus/measurementtool/GraphsCard.java @@ -25,8 +25,6 @@ import net.osmand.plus.R; import net.osmand.plus.activities.MapActivity; import net.osmand.plus.helpers.GpxUiHelper; import net.osmand.plus.helpers.GpxUiHelper.LineGraphType; -import net.osmand.plus.helpers.GpxUiHelper.GPXDataSetAxisType; -import net.osmand.plus.helpers.GpxUiHelper.OrderedLineDataSet; import net.osmand.plus.mapcontextmenu.other.HorizontalSelectionAdapter; import net.osmand.plus.mapcontextmenu.other.HorizontalSelectionAdapter.HorizontalSelectionAdapterListener; import net.osmand.plus.mapcontextmenu.other.TrackDetailsMenu; @@ -47,6 +45,9 @@ import java.util.List; import static net.osmand.GPXUtilities.GPXFile; import static net.osmand.GPXUtilities.GPXTrackAnalysis; +import static net.osmand.plus.helpers.GpxUiHelper.LineGraphType.ALTITUDE; +import static net.osmand.plus.helpers.GpxUiHelper.LineGraphType.SLOPE; +import static net.osmand.plus.helpers.GpxUiHelper.LineGraphType.SPEED; import static net.osmand.plus.mapcontextmenu.other.HorizontalSelectionAdapter.HorizontalSelectionItem; import static net.osmand.router.RouteStatisticsHelper.RouteStatistics; @@ -57,9 +58,9 @@ public class GraphsCard extends BaseCard implements OnUpdateAdditionalInfoListen private MeasurementEditingContext editingCtx; private MeasurementToolFragment fragment; - - private GraphType visibleType; - private List graphTypes = new ArrayList<>(); + private TrackDetailsMenu trackDetailsMenu; + private GPXFile gpxFile; + private GPXTrackAnalysis analysis; private GpxDisplayItem gpxItem; private View commonGraphContainer; @@ -69,24 +70,8 @@ public class GraphsCard extends BaseCard implements OnUpdateAdditionalInfoListen private CustomGraphAdapter customGraphAdapter; private RecyclerView graphTypesMenu; - private TrackDetailsMenu trackDetailsMenu; - - private enum CommonGraphType { - OVERVIEW(R.string.shared_string_overview, true, LineGraphType.ALTITUDE, LineGraphType.SLOPE), - ALTITUDE(R.string.altitude, true, LineGraphType.ALTITUDE), - SLOPE(R.string.shared_string_slope, true, LineGraphType.SLOPE), - SPEED(R.string.map_widget_speed, false, LineGraphType.SPEED); - - CommonGraphType(int titleId, boolean canBeCalculated, LineGraphType ... lineGraphTypes) { - this.titleId = titleId; - this.canBeCalculated = canBeCalculated; - this.lineGraphTypes = lineGraphTypes; - } - - final int titleId; - final boolean canBeCalculated; - final LineGraphType[] lineGraphTypes; - } + private GraphType visibleType; + private List graphTypes = new ArrayList<>(); public GraphsCard(@NonNull MapActivity mapActivity, TrackDetailsMenu trackDetailsMenu, @@ -137,23 +122,23 @@ public class GraphsCard extends BaseCard implements OnUpdateAdditionalInfoListen private void fullUpdate() { if (!isRouteCalculating()) { updateData(); - updateVisibleType(); - updateTypesMenu(); + setupVisibleType(); + updateMenu(); } - updateInfoView(); + updateView(); } - private void updateTypesMenu() { + private void updateMenu() { if (!editingCtx.isPointsEnoughToCalculateRoute()) { graphTypesMenu.setVisibility(View.GONE); } else { graphTypesMenu.setVisibility(View.VISIBLE); graphTypesMenu.removeAllViews(); - fillInTypesMenu(); + fillInMenu(); } } - private void fillInTypesMenu() { + private void fillInMenu() { OsmandApplication app = getMyApplication(); int activeColorId = nightMode ? R.color.active_color_primary_dark : R.color.active_color_primary_light; final HorizontalSelectionAdapter adapter = new HorizontalSelectionAdapter(app, nightMode); @@ -176,7 +161,7 @@ public class GraphsCard extends BaseCard implements OnUpdateAdditionalInfoListen adapter.setItems(items); adapter.setSelectedItem(item); GraphType chosenType = (GraphType) item.getObject(); - if (!isCurrentVisibleType(chosenType)) { + if (!isVisibleType(chosenType)) { changeVisibleType(chosenType); } } @@ -187,10 +172,10 @@ public class GraphsCard extends BaseCard implements OnUpdateAdditionalInfoListen private void changeVisibleType(GraphType type) { visibleType = type; - updateInfoView(); + updateView(); } - private boolean isCurrentVisibleType(GraphType type) { + private boolean isVisibleType(GraphType type) { if (visibleType != null && type != null) { return Algorithms.objectEquals(visibleType.getTitle(), type.getTitle()); } @@ -206,7 +191,7 @@ public class GraphsCard extends BaseCard implements OnUpdateAdditionalInfoListen return null; } - private void updateInfoView() { + private void updateView() { hideAll(); if (!editingCtx.isPointsEnoughToCalculateRoute()) { showMessage(app.getString(R.string.message_you_need_add_two_points_to_show_graphs)); @@ -279,54 +264,59 @@ public class GraphsCard extends BaseCard implements OnUpdateAdditionalInfoListen CustomGraphType customGraphType = (CustomGraphType) visibleType; customGraphContainer.setVisibility(View.VISIBLE); customGraphAdapter.setLegendViewType(LegendViewType.ONE_ELEMENT); - customGraphAdapter.fullUpdate((BarData) customGraphType.getGraphData(), customGraphType.getStatistics()); + customGraphAdapter.updateContent(customGraphType.getChartData(), customGraphType.getStatistics()); } else { + CommonGraphType commonGraphType = (CommonGraphType) visibleType; commonGraphContainer.setVisibility(View.VISIBLE); customGraphAdapter.setLegendViewType(LegendViewType.GONE); - commonGraphAdapter.fullUpdate((LineData) visibleType.getGraphData(), gpxItem); + commonGraphAdapter.updateContent(commonGraphType.getChartData(), gpxItem); } } private void updateData() { graphTypes.clear(); OsmandApplication app = getMyApplication(); - GPXFile gpxFile = getGpxFile(); - GPXTrackAnalysis analysis = gpxFile != null ? gpxFile.getAnalysis(0) : null; + gpxFile = getGpxFile(); + analysis = gpxFile != null ? gpxFile.getAnalysis(0) : null; gpxItem = gpxFile != null ? GpxUiHelper.makeGpxDisplayItem(app, gpxFile) : null; if (gpxItem != null) { trackDetailsMenu.setGpxItem(gpxItem); } + if (analysis == null) return; // update common graph data - for (CommonGraphType commonType : CommonGraphType.values()) { - List dataSets = GpxUiHelper.getDataSets(commonGraphAdapter.getChart(), - app, analysis, false, commonType.lineGraphTypes); - LineData data = !Algorithms.isEmpty(dataSets) ? new LineData(dataSets) : null; - String title = app.getString(commonType.titleId); - graphTypes.add(new GraphType(title, commonType.canBeCalculated, data)); - } + boolean hasElevationData = analysis.hasElevationData; + boolean hasSpeedData = analysis.isSpeedSpecified(); + addCommonType(R.string.shared_string_overview, true, hasElevationData, ALTITUDE, SLOPE); + addCommonType(R.string.altitude, true, hasElevationData, ALTITUDE); + addCommonType(R.string.shared_string_slope, true, hasElevationData, SLOPE); + addCommonType(R.string.map_widget_speed, false, hasSpeedData, SPEED); // update custom graph data List routeStatistics = calculateRouteStatistics(); if (analysis != null && routeStatistics != null) { for (RouteStatistics statistics : routeStatistics) { String title = AndroidUtils.getStringRouteInfoPropertyValue(app, statistics.name); - BarData data = null; - if (!Algorithms.isEmpty(statistics.elements)) { - data = GpxUiHelper.buildStatisticChart(app, customGraphAdapter.getChart(), - statistics, analysis, true, nightMode); - } - graphTypes.add(new CustomGraphType(title, false, data, statistics)); + graphTypes.add(new CustomGraphType(title, statistics)); } } } - private void updateVisibleType() { + private void addCommonType(int titleId, + boolean canBeCalculated, + boolean hasData, + LineGraphType ... lineGraphTypes) { + OsmandApplication app = getMyApplication(); + String title = app.getString(titleId); + graphTypes.add(new CommonGraphType(title, canBeCalculated, hasData, lineGraphTypes)); + } + + private void setupVisibleType() { if (visibleType == null) { visibleType = getFirstAvailableType(); } else { for (GraphType type : graphTypes) { - if (isCurrentVisibleType(type)) { + if (isVisibleType(type)) { visibleType = type.isAvailable() ? type : getFirstAvailableType(); break; } @@ -346,23 +336,23 @@ public class GraphsCard extends BaseCard implements OnUpdateAdditionalInfoListen private List calculateRouteStatistics() { OsmandApplication app = getMyApplication(); List route = editingCtx.getAllRouteSegments(); - if (route == null || app == null) return null; - return RouteDetailsFragment.calculateRouteStatistics(app, route, nightMode); + if (route != null && app != null) { + return RouteDetailsFragment.calculateRouteStatistics(app, route, nightMode); + } + return null; } private boolean isRouteCalculating() { return fragment.isProgressBarVisible(); } - private class GraphType { + private abstract class GraphType { private String title; private boolean canBeCalculated; - private ChartData graphData; - public GraphType(String title, boolean canBeCalculated, ChartData graphData) { + public GraphType(String title, boolean canBeCalculated) { this.title = title; this.canBeCalculated = canBeCalculated; - this.graphData = graphData; } public String getTitle() { @@ -385,26 +375,63 @@ public class GraphsCard extends BaseCard implements OnUpdateAdditionalInfoListen return canBeCalculated; } - public boolean hasData() { - return getGraphData() != null; + public abstract boolean hasData(); + + public abstract T getChartData(); + } + + private class CommonGraphType extends GraphType { + + private boolean hasData; + private LineGraphType[] lineGraphTypes; + + public CommonGraphType(String title, boolean canBeCalculated, boolean hasData, LineGraphType ... lineGraphTypes) { + super(title, canBeCalculated); + this.hasData = hasData; + this.lineGraphTypes = lineGraphTypes; } - public ChartData getGraphData() { - return graphData; + @Override + public boolean hasData() { + return hasData; + } + + @Override + public LineData getChartData() { + GpxUiHelper.setupGPXChart(commonGraphAdapter.getChart(), 4, 24f, 16f, !nightMode, true); + List dataSets = GpxUiHelper.getDataSets(commonGraphAdapter.getChart(), + app, analysis, false, lineGraphTypes); + return !Algorithms.isEmpty(dataSets) ? new LineData(dataSets) : null; } } - private class CustomGraphType extends GraphType { + private class CustomGraphType extends GraphType { private RouteStatistics statistics; - public CustomGraphType(String title, boolean canBeCalculated, ChartData graphData, RouteStatistics statistics) { - super(title, canBeCalculated, graphData); + public CustomGraphType(String title, RouteStatistics statistics) { + super(title, false); this.statistics = statistics; } public RouteStatistics getStatistics() { return statistics; } + + @Override + public boolean hasData() { + return !Algorithms.isEmpty(statistics.elements); + } + + @Override + public BarData getChartData() { + GpxUiHelper.setupHorizontalGPXChart(app, customGraphAdapter.getChart(), 5, 9, 24, true, nightMode); + BarData data = null; + if (!Algorithms.isEmpty(statistics.elements)) { + data = GpxUiHelper.buildStatisticChart(app, customGraphAdapter.getChart(), + statistics, analysis, true, nightMode); + } + return data; + } } } diff --git a/OsmAnd/src/net/osmand/plus/measurementtool/graph/BaseGraphAdapter.java b/OsmAnd/src/net/osmand/plus/measurementtool/graph/BaseGraphAdapter.java index dc83460a3f..5f9e1fdc29 100644 --- a/OsmAnd/src/net/osmand/plus/measurementtool/graph/BaseGraphAdapter.java +++ b/OsmAnd/src/net/osmand/plus/measurementtool/graph/BaseGraphAdapter.java @@ -10,27 +10,27 @@ import com.github.mikephil.charting.listener.ChartTouchListener; import net.osmand.plus.OsmandApplication; -public abstract class BaseGraphAdapter { +public abstract class BaseGraphAdapter<_Chart extends Chart, _ChartData extends ChartData, _Data> { private Highlight lastKnownHighlight; - protected CHART mChart; - protected CHART_DATA mChartData; - protected DATA mAdditionalData; + protected _Chart chart; + protected _ChartData chartData; + protected _Data additionalData; protected boolean usedOnMap; - public BaseGraphAdapter(CHART chart, boolean usedOnMap) { - this.mChart = chart; + public BaseGraphAdapter(_Chart chart, boolean usedOnMap) { + this.chart = chart; this.usedOnMap = usedOnMap; - prepareCharterView(); + prepareChartView(); } - protected void prepareCharterView() { - mChart.setExtraRightOffset(16); - mChart.setExtraLeftOffset(16); + protected void prepareChartView() { + chart.setExtraRightOffset(16); + chart.setExtraLeftOffset(16); } - public CHART getChart() { - return mChart; + public _Chart getChart() { + return chart; } protected void updateHighlight() { @@ -41,14 +41,14 @@ public abstract class BaseGraphAdapter 0) { - highlightDrawX = mChart.getHighlighted()[0].getDrawX(); + if (chart.getHighlighted() != null && chart.getHighlighted().length > 0) { + highlightDrawX = chart.getHighlighted()[0].getDrawX(); } else { highlightDrawX = -1; } @@ -68,8 +67,8 @@ public class CommonGraphAdapter extends BaseGraphAdapter 0) { gpxItem.chartHighlightPos = highlights[0].getX(); } else { @@ -104,17 +103,9 @@ public class CommonGraphAdapter extends BaseGraphAdapter 0) { - gpxItem = group.getModifiableList().get(0); - if (gpxItem != null) { - gpxItem.route = true; - } - } + gpxItem = GpxUiHelper.makeGpxDisplayItem(app, gpx); } void openDetails() { diff --git a/OsmAnd/src/net/osmand/plus/routepreparationmenu/cards/RouteInfoCard.java b/OsmAnd/src/net/osmand/plus/routepreparationmenu/cards/RouteInfoCard.java index c2cbec6f7b..124687f2f7 100644 --- a/OsmAnd/src/net/osmand/plus/routepreparationmenu/cards/RouteInfoCard.java +++ b/OsmAnd/src/net/osmand/plus/routepreparationmenu/cards/RouteInfoCard.java @@ -41,6 +41,7 @@ public class RouteInfoCard extends BaseCard { updateHeader(); LinearLayout container = (LinearLayout) view.findViewById(R.id.route_items); HorizontalBarChart chart = (HorizontalBarChart) view.findViewById(R.id.chart); + GpxUiHelper.setupHorizontalGPXChart(getMyApplication(), chart, 5, 9, 24, true, nightMode); BarData barData = GpxUiHelper.buildStatisticChart(app, chart, statistics, analysis, true, nightMode); graphAdapter = new CustomGraphAdapter(chart, true); graphAdapter.setLegendContainer(container); diff --git a/OsmAnd/src/net/osmand/plus/routepreparationmenu/cards/RouteStatisticCard.java b/OsmAnd/src/net/osmand/plus/routepreparationmenu/cards/RouteStatisticCard.java index bb6483155b..e25c242469 100644 --- a/OsmAnd/src/net/osmand/plus/routepreparationmenu/cards/RouteStatisticCard.java +++ b/OsmAnd/src/net/osmand/plus/routepreparationmenu/cards/RouteStatisticCard.java @@ -217,6 +217,7 @@ public class RouteStatisticCard extends BaseCard { private void buildHeader(GPXTrackAnalysis analysis) { LineChart mChart = (LineChart) view.findViewById(R.id.chart); + GpxUiHelper.setupGPXChart(mChart, 4, 24f, 16f, !nightMode, true); graphAdapter = new CommonGraphAdapter(mChart, true); if (analysis.hasElevationData) { @@ -235,7 +236,7 @@ public class RouteStatisticCard extends BaseCard { this.elevationDataSet = elevationDataSet; this.slopeDataSet = slopeDataSet; - graphAdapter.fullUpdate(new LineData(dataSets), gpxItem); + graphAdapter.updateContent(new LineData(dataSets), gpxItem); mChart.setVisibility(View.VISIBLE); } else { mChart.setVisibility(View.GONE); From beddaf82090a381b8ac244a18df90aee2399e94b Mon Sep 17 00:00:00 2001 From: nazar-kutz Date: Thu, 5 Nov 2020 01:02:16 +0200 Subject: [PATCH 6/8] Fix chart on map --- .../other/TrackDetailsMenu.java | 11 ++++-- .../plus/measurementtool/GraphsCard.java | 21 +++++++++- .../MeasurementToolFragment.java | 13 ++++++- .../plus/measurementtool/PointsCard.java | 4 +- .../graph/GraphAdapterHelper.java | 38 +++++++++++-------- .../RouteDetailsFragment.java | 11 ++---- .../osmand/plus/views/layers/RouteLayer.java | 16 +++++++- 7 files changed, 82 insertions(+), 32 deletions(-) diff --git a/OsmAnd/src/net/osmand/plus/mapcontextmenu/other/TrackDetailsMenu.java b/OsmAnd/src/net/osmand/plus/mapcontextmenu/other/TrackDetailsMenu.java index 1e441071a4..a45bfb27e8 100644 --- a/OsmAnd/src/net/osmand/plus/mapcontextmenu/other/TrackDetailsMenu.java +++ b/OsmAnd/src/net/osmand/plus/mapcontextmenu/other/TrackDetailsMenu.java @@ -125,8 +125,7 @@ public class TrackDetailsMenu { hidding = true; fragment.dismiss(backPressed); } else { - segment = null; - trackChartPoints = null; + reset(); } } @@ -274,8 +273,7 @@ public class TrackDetailsMenu { if (hidding) { hidding = false; visible = false; - segment = null; - trackChartPoints = null; + reset(); } } @@ -532,6 +530,11 @@ public class TrackDetailsMenu { fitTrackOnMap(chart, location, forceFit); } + public void reset() { + segment = null; + trackChartPoints = null; + } + private List getXAxisPoints(LineChart chart) { float[] entries = chart.getXAxis().mEntries; LineData lineData = chart.getLineData(); diff --git a/OsmAnd/src/net/osmand/plus/measurementtool/GraphsCard.java b/OsmAnd/src/net/osmand/plus/measurementtool/GraphsCard.java index 69277f19a6..3b2e979f55 100644 --- a/OsmAnd/src/net/osmand/plus/measurementtool/GraphsCard.java +++ b/OsmAnd/src/net/osmand/plus/measurementtool/GraphsCard.java @@ -34,6 +34,7 @@ import net.osmand.plus.measurementtool.graph.CustomGraphAdapter; import net.osmand.plus.measurementtool.graph.CustomGraphAdapter.LegendViewType; import net.osmand.plus.measurementtool.graph.BaseGraphAdapter; import net.osmand.plus.measurementtool.graph.GraphAdapterHelper; +import net.osmand.plus.measurementtool.graph.GraphAdapterHelper.RefreshMapCallback; import net.osmand.plus.routepreparationmenu.RouteDetailsFragment; import net.osmand.plus.routepreparationmenu.cards.BaseCard; import net.osmand.router.RouteSegmentResult; @@ -59,6 +60,7 @@ public class GraphsCard extends BaseCard implements OnUpdateAdditionalInfoListen private MeasurementEditingContext editingCtx; private MeasurementToolFragment fragment; private TrackDetailsMenu trackDetailsMenu; + private RefreshMapCallback refreshMapCallback; private GPXFile gpxFile; private GPXTrackAnalysis analysis; private GpxDisplayItem gpxItem; @@ -105,7 +107,7 @@ public class GraphsCard extends BaseCard implements OnUpdateAdditionalInfoListen }); GraphAdapterHelper.bindGraphAdapters(commonGraphAdapter, Arrays.asList((BaseGraphAdapter) customGraphAdapter), (ViewGroup) view); - GraphAdapterHelper.bindToMap(commonGraphAdapter, mapActivity, trackDetailsMenu); + refreshMapCallback = GraphAdapterHelper.bindToMap(commonGraphAdapter, mapActivity, trackDetailsMenu); fullUpdate(); } @@ -116,7 +118,9 @@ public class GraphsCard extends BaseCard implements OnUpdateAdditionalInfoListen @Override public void onUpdateAdditionalInfo() { - fullUpdate(); + if (editingCtx != null) { + fullUpdate(); + } } private void fullUpdate() { @@ -126,6 +130,7 @@ public class GraphsCard extends BaseCard implements OnUpdateAdditionalInfoListen updateMenu(); } updateView(); + updateChartOnMap(); } private void updateMenu() { @@ -302,6 +307,13 @@ public class GraphsCard extends BaseCard implements OnUpdateAdditionalInfoListen } } + private void updateChartOnMap() { + if (hasVisibleGraph()) { + trackDetailsMenu.reset(); + refreshMapCallback.refreshMap(false); + } + } + private void addCommonType(int titleId, boolean canBeCalculated, boolean hasData, @@ -346,6 +358,11 @@ public class GraphsCard extends BaseCard implements OnUpdateAdditionalInfoListen return fragment.isProgressBarVisible(); } + public boolean hasVisibleGraph() { + return (commonGraphContainer != null && commonGraphContainer.getVisibility() == View.VISIBLE) + || (customGraphContainer != null && customGraphContainer.getVisibility() == View.VISIBLE); + } + private abstract class GraphType { private String title; private boolean canBeCalculated; diff --git a/OsmAnd/src/net/osmand/plus/measurementtool/MeasurementToolFragment.java b/OsmAnd/src/net/osmand/plus/measurementtool/MeasurementToolFragment.java index c92d028f02..16c8e2309c 100644 --- a/OsmAnd/src/net/osmand/plus/measurementtool/MeasurementToolFragment.java +++ b/OsmAnd/src/net/osmand/plus/measurementtool/MeasurementToolFragment.java @@ -553,8 +553,13 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route } private void updateAdditionalInfoView() { - if (visibleCard instanceof OnUpdateAdditionalInfoListener) { - ((OnUpdateAdditionalInfoListener) visibleCard).onUpdateAdditionalInfo(); + updateAdditionalInfoView(pointsCard); + updateAdditionalInfoView(graphsCard); + } + + private void updateAdditionalInfoView(OnUpdateAdditionalInfoListener listener) { + if (listener != null) { + listener.onUpdateAdditionalInfo(); } } @@ -1514,6 +1519,10 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route return type.equals(currentAdditionalInfoType); } + public boolean hasVisibleGraph() { + return graphsCard != null && graphsCard.hasVisibleGraph(); + } + private String getSuggestedFileName() { GpxData gpxData = editingCtx.getGpxData(); String displayedName = null; diff --git a/OsmAnd/src/net/osmand/plus/measurementtool/PointsCard.java b/OsmAnd/src/net/osmand/plus/measurementtool/PointsCard.java index 9d14598856..f3b55473bc 100644 --- a/OsmAnd/src/net/osmand/plus/measurementtool/PointsCard.java +++ b/OsmAnd/src/net/osmand/plus/measurementtool/PointsCard.java @@ -24,7 +24,9 @@ public class PointsCard extends BaseCard implements OnUpdateAdditionalInfoListen @Override public void onUpdateAdditionalInfo() { - adapter.notifyDataSetChanged(); + if (adapter != null) { + adapter.notifyDataSetChanged(); + } } @Override diff --git a/OsmAnd/src/net/osmand/plus/measurementtool/graph/GraphAdapterHelper.java b/OsmAnd/src/net/osmand/plus/measurementtool/graph/GraphAdapterHelper.java index efacad9d50..69eb02eec7 100644 --- a/OsmAnd/src/net/osmand/plus/measurementtool/graph/GraphAdapterHelper.java +++ b/OsmAnd/src/net/osmand/plus/measurementtool/graph/GraphAdapterHelper.java @@ -5,6 +5,8 @@ import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; +import androidx.annotation.NonNull; + import com.github.mikephil.charting.charts.BarChart; import com.github.mikephil.charting.charts.LineChart; import com.github.mikephil.charting.data.Entry; @@ -96,15 +98,27 @@ public class GraphAdapterHelper { } } - public static void bindToMap(final CommonGraphAdapter graphAdapter, - final MapActivity mapActivity, - final TrackDetailsMenu detailsMenu) { + public static RefreshMapCallback bindToMap(@NonNull final CommonGraphAdapter graphAdapter, + @NonNull final MapActivity mapActivity, + @NonNull final TrackDetailsMenu detailsMenu) { + final RefreshMapCallback refreshMapCallback = new RefreshMapCallback() { + @Override + public void refreshMap(boolean forceFit) { + LineChart chart = graphAdapter.getChart(); + OsmandApplication app = mapActivity.getMyApplication(); + if (!app.getRoutingHelper().isFollowingMode()) { + detailsMenu.refreshChart(chart, forceFit); + mapActivity.refreshMap(); + } + } + }; + graphAdapter.addValueSelectedListener(BIND_TO_MAP_KEY, new CommonGraphAdapter.ExternalValueSelectedListener() { @Override public void onValueSelected(Entry e, Highlight h) { - refreshChart(mapActivity, graphAdapter.getChart(), detailsMenu, false); + refreshMapCallback.refreshMap(false); } @Override @@ -125,21 +139,15 @@ public class GraphAdapterHelper { lastPerformedGesture == ChartTouchListener.ChartGesture.PINCH_ZOOM || lastPerformedGesture == ChartTouchListener.ChartGesture.DOUBLE_TAP || lastPerformedGesture == ChartTouchListener.ChartGesture.ROTATE) { - refreshChart(mapActivity, graphAdapter.getChart(), detailsMenu, true); + refreshMapCallback.refreshMap(true); } } }); + + return refreshMapCallback; } - public static void refreshChart(MapActivity mapActivity, - LineChart chart, - TrackDetailsMenu menu, - boolean forceFit) { - if (mapActivity == null || chart == null || menu == null) return; - OsmandApplication app = mapActivity.getMyApplication(); - if (!app.getRoutingHelper().isFollowingMode()) { - menu.refreshChart(chart, forceFit); - mapActivity.refreshMap(); - } + public interface RefreshMapCallback { + void refreshMap(boolean forceFit); } } diff --git a/OsmAnd/src/net/osmand/plus/routepreparationmenu/RouteDetailsFragment.java b/OsmAnd/src/net/osmand/plus/routepreparationmenu/RouteDetailsFragment.java index c6dcac20a6..780186b068 100644 --- a/OsmAnd/src/net/osmand/plus/routepreparationmenu/RouteDetailsFragment.java +++ b/OsmAnd/src/net/osmand/plus/routepreparationmenu/RouteDetailsFragment.java @@ -60,6 +60,7 @@ import net.osmand.plus.mapcontextmenu.other.TrackDetailsMenu; import net.osmand.plus.measurementtool.graph.BaseGraphAdapter; import net.osmand.plus.measurementtool.graph.CommonGraphAdapter; import net.osmand.plus.measurementtool.graph.GraphAdapterHelper; +import net.osmand.plus.measurementtool.graph.GraphAdapterHelper.RefreshMapCallback; import net.osmand.plus.render.MapRenderRepositories; import net.osmand.plus.routepreparationmenu.cards.BaseCard; import net.osmand.plus.routepreparationmenu.cards.BaseCard.CardListener; @@ -113,6 +114,7 @@ public class RouteDetailsFragment extends ContextMenuFragment private RouteStatisticCard statisticCard; private List routeInfoCards = new ArrayList<>(); private RouteDetailsMenu routeDetailsMenu; + private RefreshMapCallback refreshMapCallback; public interface RouteDetailsFragmentListener { void onNavigationRequested(); @@ -336,7 +338,7 @@ public class RouteDetailsFragment extends ContextMenuFragment CommonGraphAdapter mainGraphAdapter = statisticCard.getGraphAdapter(); if (mainGraphAdapter != null) { GraphAdapterHelper.bindGraphAdapters(mainGraphAdapter, getRouteInfoCardsGraphAdapters(), getMainView()); - GraphAdapterHelper.bindToMap(mainGraphAdapter, mapActivity, routeDetailsMenu); + refreshMapCallback = GraphAdapterHelper.bindToMap(mainGraphAdapter, mapActivity, routeDetailsMenu); } } @@ -369,12 +371,7 @@ public class RouteDetailsFragment extends ContextMenuFragment protected void calculateLayout(View view, boolean initLayout) { super.calculateLayout(view, initLayout); if (!initLayout && getCurrentMenuState() != MenuState.FULL_SCREEN) { - MapActivity mapActivity = getMapActivity(); - CommonGraphAdapter mainGraphAdapter = statisticCard.getGraphAdapter(); - if (mainGraphAdapter != null) { - LineChart chart = mainGraphAdapter.getChart(); - GraphAdapterHelper.refreshChart(mapActivity, chart, routeDetailsMenu, false); - } + refreshMapCallback.refreshMap(false); } } diff --git a/OsmAnd/src/net/osmand/plus/views/layers/RouteLayer.java b/OsmAnd/src/net/osmand/plus/views/layers/RouteLayer.java index 8491f24a67..f966b7d897 100644 --- a/OsmAnd/src/net/osmand/plus/views/layers/RouteLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/layers/RouteLayer.java @@ -26,7 +26,9 @@ import net.osmand.data.QuadRect; import net.osmand.data.RotatedTileBox; import net.osmand.data.TransportStop; import net.osmand.plus.R; +import net.osmand.plus.activities.MapActivity; import net.osmand.plus.mapcontextmenu.other.TrackChartPoints; +import net.osmand.plus.measurementtool.MeasurementToolFragment; import net.osmand.plus.profiles.LocationIcon; import net.osmand.plus.routing.RouteCalculationResult; import net.osmand.plus.routing.RouteDirectionInfo; @@ -158,7 +160,8 @@ public class RouteLayer extends OsmandMapLayer implements ContextMenuLayer.ICont @Override public void onPrepareBufferImage(Canvas canvas, RotatedTileBox tileBox, DrawSettings settings) { if ((helper.isPublicTransportMode() && transportHelper.getRoutes() != null) || - (helper.getFinalLocation() != null && helper.getRoute().isCalculated())) { + (helper.getFinalLocation() != null && helper.getRoute().isCalculated()) || + isPlanRouteGraphsAvailable()) { updateAttrs(settings, tileBox); @@ -202,6 +205,17 @@ public class RouteLayer extends OsmandMapLayer implements ContextMenuLayer.ICont } + private boolean isPlanRouteGraphsAvailable() { + if (view.getContext() instanceof MapActivity) { + MapActivity mapActivity = (MapActivity) view.getContext(); + MeasurementToolFragment fragment = mapActivity.getMeasurementToolFragment(); + if (fragment != null) { + return fragment.hasVisibleGraph(); + } + } + return false; + } + private void updateAttrs(DrawSettings settings, RotatedTileBox tileBox) { boolean updatePaints = attrs.updatePaints(view.getApplication(), settings, tileBox); attrs.isPaint3 = false; From bd45e5aba8464835fdacc69c7b10d1b8e3e62a79 Mon Sep 17 00:00:00 2001 From: nazar-kutz Date: Thu, 5 Nov 2020 01:37:05 +0200 Subject: [PATCH 7/8] Show only two graphs on chart, refactor getDataSets --- .../net/osmand/plus/helpers/GpxUiHelper.java | 66 +++++++------------ .../plus/measurementtool/GraphsCard.java | 22 ++++--- .../plus/myplaces/TrackSegmentFragment.java | 10 +-- 3 files changed, 43 insertions(+), 55 deletions(-) diff --git a/OsmAnd/src/net/osmand/plus/helpers/GpxUiHelper.java b/OsmAnd/src/net/osmand/plus/helpers/GpxUiHelper.java index dd2a35a9a4..2b91e0a6b7 100644 --- a/OsmAnd/src/net/osmand/plus/helpers/GpxUiHelper.java +++ b/OsmAnd/src/net/osmand/plus/helpers/GpxUiHelper.java @@ -2056,52 +2056,34 @@ public class GpxUiHelper { public static List getDataSets(LineChart chart, OsmandApplication app, GPXTrackAnalysis analysis, - boolean calcWithoutGaps, - LineGraphType ... types) { - if (app == null || chart == null || analysis == null || types == null) { + @NonNull LineGraphType firstType, + @Nullable LineGraphType secondType, + boolean calcWithoutGaps) { + if (app == null || chart == null || analysis == null) { return new ArrayList<>(); } - if (types.length > 1) { - return getDataSetsImpl(chart, app, analysis, calcWithoutGaps, types[0], types[1]); - } else { - return getDataSetsImpl(chart, app, analysis, calcWithoutGaps, types[0]); - } - } - - private static List getDataSetsImpl(@NonNull LineChart chart, - @NonNull OsmandApplication app, - @NonNull GPXTrackAnalysis analysis, - boolean calcWithoutGaps, - @NonNull LineGraphType type) { List result = new ArrayList<>(); - ILineDataSet dataSet = getDataSet(chart, app, analysis, calcWithoutGaps, false, type); - if (dataSet != null) { - result.add(dataSet); - } - return result; - } - - private static List getDataSetsImpl(@NonNull LineChart chart, - @NonNull OsmandApplication app, - @NonNull GPXTrackAnalysis analysis, - boolean calcWithoutGaps, - @NonNull LineGraphType type1, - @NonNull LineGraphType type2) { - List result = new ArrayList<>(); - OrderedLineDataSet dataSet1 = getDataSet(chart, app, analysis, calcWithoutGaps, false, type1); - OrderedLineDataSet dataSet2 = getDataSet(chart, app, analysis, calcWithoutGaps, true, type2); - if (dataSet1 == null && dataSet2 == null) { - return new ArrayList<>(); - } else if (dataSet1 == null) { - result.add(dataSet2); - } else if (dataSet2 == null) { - result.add(dataSet1); - } else if (dataSet1.getPriority() < dataSet2.getPriority()) { - result.add(dataSet2); - result.add(dataSet1); + if (secondType == null) { + ILineDataSet dataSet = getDataSet(chart, app, analysis, calcWithoutGaps, false, firstType); + if (dataSet != null) { + result.add(dataSet); + } } else { - result.add(dataSet1); - result.add(dataSet2); + OrderedLineDataSet dataSet1 = getDataSet(chart, app, analysis, calcWithoutGaps, false, firstType); + OrderedLineDataSet dataSet2 = getDataSet(chart, app, analysis, calcWithoutGaps, true, secondType); + if (dataSet1 == null && dataSet2 == null) { + return new ArrayList<>(); + } else if (dataSet1 == null) { + result.add(dataSet2); + } else if (dataSet2 == null) { + result.add(dataSet1); + } else if (dataSet1.getPriority() < dataSet2.getPriority()) { + result.add(dataSet2); + result.add(dataSet1); + } else { + result.add(dataSet1); + result.add(dataSet2); + } } return result; } diff --git a/OsmAnd/src/net/osmand/plus/measurementtool/GraphsCard.java b/OsmAnd/src/net/osmand/plus/measurementtool/GraphsCard.java index 3b2e979f55..9a64d77433 100644 --- a/OsmAnd/src/net/osmand/plus/measurementtool/GraphsCard.java +++ b/OsmAnd/src/net/osmand/plus/measurementtool/GraphsCard.java @@ -8,6 +8,7 @@ import android.widget.TextView; import androidx.annotation.DrawableRes; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; @@ -293,9 +294,9 @@ public class GraphsCard extends BaseCard implements OnUpdateAdditionalInfoListen boolean hasElevationData = analysis.hasElevationData; boolean hasSpeedData = analysis.isSpeedSpecified(); addCommonType(R.string.shared_string_overview, true, hasElevationData, ALTITUDE, SLOPE); - addCommonType(R.string.altitude, true, hasElevationData, ALTITUDE); - addCommonType(R.string.shared_string_slope, true, hasElevationData, SLOPE); - addCommonType(R.string.map_widget_speed, false, hasSpeedData, SPEED); + addCommonType(R.string.altitude, true, hasElevationData, ALTITUDE, null); + addCommonType(R.string.shared_string_slope, true, hasElevationData, SLOPE, null); + addCommonType(R.string.map_widget_speed, false, hasSpeedData, SPEED, null); // update custom graph data List routeStatistics = calculateRouteStatistics(); @@ -317,10 +318,11 @@ public class GraphsCard extends BaseCard implements OnUpdateAdditionalInfoListen private void addCommonType(int titleId, boolean canBeCalculated, boolean hasData, - LineGraphType ... lineGraphTypes) { + LineGraphType firstType, + LineGraphType secondType) { OsmandApplication app = getMyApplication(); String title = app.getString(titleId); - graphTypes.add(new CommonGraphType(title, canBeCalculated, hasData, lineGraphTypes)); + graphTypes.add(new CommonGraphType(title, canBeCalculated, hasData, firstType, secondType)); } private void setupVisibleType() { @@ -400,12 +402,14 @@ public class GraphsCard extends BaseCard implements OnUpdateAdditionalInfoListen private class CommonGraphType extends GraphType { private boolean hasData; - private LineGraphType[] lineGraphTypes; + private LineGraphType firstType; + private LineGraphType secondType; - public CommonGraphType(String title, boolean canBeCalculated, boolean hasData, LineGraphType ... lineGraphTypes) { + public CommonGraphType(String title, boolean canBeCalculated, boolean hasData, @NonNull LineGraphType firstType, @Nullable LineGraphType secondType) { super(title, canBeCalculated); this.hasData = hasData; - this.lineGraphTypes = lineGraphTypes; + this.firstType = firstType; + this.secondType = secondType; } @Override @@ -417,7 +421,7 @@ public class GraphsCard extends BaseCard implements OnUpdateAdditionalInfoListen public LineData getChartData() { GpxUiHelper.setupGPXChart(commonGraphAdapter.getChart(), 4, 24f, 16f, !nightMode, true); List dataSets = GpxUiHelper.getDataSets(commonGraphAdapter.getChart(), - app, analysis, false, lineGraphTypes); + app, analysis, firstType, secondType, false); return !Algorithms.isEmpty(dataSets) ? new LineData(dataSets) : null; } } diff --git a/OsmAnd/src/net/osmand/plus/myplaces/TrackSegmentFragment.java b/OsmAnd/src/net/osmand/plus/myplaces/TrackSegmentFragment.java index 7a7178c578..e53b51cf39 100644 --- a/OsmAnd/src/net/osmand/plus/myplaces/TrackSegmentFragment.java +++ b/OsmAnd/src/net/osmand/plus/myplaces/TrackSegmentFragment.java @@ -62,6 +62,7 @@ import net.osmand.plus.activities.TrackActivity; import net.osmand.plus.base.OsmAndListFragment; import net.osmand.plus.helpers.AndroidUiHelper; import net.osmand.plus.helpers.GpxUiHelper; +import net.osmand.plus.helpers.GpxUiHelper.LineGraphType; import net.osmand.plus.helpers.GpxUiHelper.GPXDataSetAxisType; import net.osmand.plus.helpers.GpxUiHelper.GPXDataSetType; import net.osmand.plus.helpers.GpxUiHelper.OrderedLineDataSet; @@ -430,13 +431,14 @@ public class TrackSegmentFragment extends OsmAndListFragment implements TrackBit private List getDataSets(LineChart chart, GPXTabItemType tabType, - GpxUiHelper.LineGraphType... types) { + LineGraphType firstType, + LineGraphType secondType) { List dataSets = dataSetsMap.get(tabType); if (dataSets == null && chart != null) { GPXTrackAnalysis analysis = gpxItem.analysis; GpxDataItem gpxDataItem = getGpxDataItem(); boolean calcWithoutGaps = gpxItem.isGeneralTrack() && gpxDataItem != null && !gpxDataItem.isJoinSegments(); - dataSets = GpxUiHelper.getDataSets(chart, app, analysis, calcWithoutGaps, types); + dataSets = GpxUiHelper.getDataSets(chart, app, analysis, firstType, secondType, calcWithoutGaps); dataSetsMap.put(tabType, dataSets); } return dataSets; @@ -878,7 +880,7 @@ public class TrackSegmentFragment extends OsmAndListFragment implements TrackBit if (analysis != null && analysis.isSpeedSpecified()) { if (analysis.hasSpeedData) { GpxUiHelper.setupGPXChart(app, chart, 4); - chart.setData(new LineData(getDataSets(chart, GPXTabItemType.GPX_TAB_ITEM_SPEED, SPEED))); + chart.setData(new LineData(getDataSets(chart, GPXTabItemType.GPX_TAB_ITEM_SPEED, SPEED, null))); updateChart(chart); chart.setVisibility(View.VISIBLE); } else { @@ -1144,7 +1146,7 @@ public class TrackSegmentFragment extends OsmAndListFragment implements TrackBit LatLon location = null; WptPt wpt = null; gpxItem.chartTypes = null; - List ds = getDataSets(null, tabType); + List ds = getDataSets(null, tabType, null, null); if (ds != null && ds.size() > 0) { gpxItem.chartTypes = new GPXDataSetType[ds.size()]; for (int i = 0; i < ds.size(); i++) { From b1ddd40ec834a3bd188a0b5934c2a8b1d98ce977 Mon Sep 17 00:00:00 2001 From: nazar-kutz Date: Thu, 5 Nov 2020 02:04:25 +0200 Subject: [PATCH 8/8] small fixes --- .../src/net/osmand/plus/measurementtool/GraphsCard.java | 4 ++-- .../plus/measurementtool/graph/CustomGraphAdapter.java | 4 ++-- .../plus/measurementtool/graph/GraphAdapterHelper.java | 8 +++++--- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/OsmAnd/src/net/osmand/plus/measurementtool/GraphsCard.java b/OsmAnd/src/net/osmand/plus/measurementtool/GraphsCard.java index 9a64d77433..a0f74d34db 100644 --- a/OsmAnd/src/net/osmand/plus/measurementtool/GraphsCard.java +++ b/OsmAnd/src/net/osmand/plus/measurementtool/GraphsCard.java @@ -42,7 +42,7 @@ import net.osmand.router.RouteSegmentResult; import net.osmand.util.Algorithms; import java.util.ArrayList; -import java.util.Arrays; +import java.util.Collections; import java.util.List; import static net.osmand.GPXUtilities.GPXFile; @@ -107,7 +107,7 @@ public class GraphsCard extends BaseCard implements OnUpdateAdditionalInfoListen } }); - GraphAdapterHelper.bindGraphAdapters(commonGraphAdapter, Arrays.asList((BaseGraphAdapter) customGraphAdapter), (ViewGroup) view); + GraphAdapterHelper.bindGraphAdapters(commonGraphAdapter, Collections.singletonList((BaseGraphAdapter) customGraphAdapter), (ViewGroup) view); refreshMapCallback = GraphAdapterHelper.bindToMap(commonGraphAdapter, mapActivity, trackDetailsMenu); fullUpdate(); } diff --git a/OsmAnd/src/net/osmand/plus/measurementtool/graph/CustomGraphAdapter.java b/OsmAnd/src/net/osmand/plus/measurementtool/graph/CustomGraphAdapter.java index 72ccff4604..1bc763b165 100644 --- a/OsmAnd/src/net/osmand/plus/measurementtool/graph/CustomGraphAdapter.java +++ b/OsmAnd/src/net/osmand/plus/measurementtool/graph/CustomGraphAdapter.java @@ -29,7 +29,7 @@ import net.osmand.router.RouteStatisticsHelper.RouteSegmentAttribute; import net.osmand.util.Algorithms; import java.util.ArrayList; -import java.util.Arrays; +import java.util.Collections; import java.util.List; import static net.osmand.plus.track.ColorsCard.MINIMUM_CONTRAST_RATIO; @@ -122,7 +122,7 @@ public class CustomGraphAdapter extends BaseGraphAdapter