diff --git a/android/build.gradle b/android/build.gradle index 53e65df19f..da3aaffbbd 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -103,6 +103,7 @@ dependencies { implementation 'com.google.android:flexbox:1.0.0' implementation 'com.trafi:anchor-bottom-sheet-behavior:0.13-alpha' implementation 'com.google.firebase:firebase-messaging:17.0.0' + implementation 'com.github.yoksnod:MPAndroidChart:3.2.0-alpha' } def getDate() { diff --git a/android/res/drawable-hdpi/elevation.png b/android/res/drawable-hdpi/elevation.png new file mode 100755 index 0000000000..326dac5721 Binary files /dev/null and b/android/res/drawable-hdpi/elevation.png differ diff --git a/android/res/drawable-hdpi/ic_chart_pin.png b/android/res/drawable-hdpi/ic_chart_pin.png new file mode 100755 index 0000000000..5c8fc4d0b9 Binary files /dev/null and b/android/res/drawable-hdpi/ic_chart_pin.png differ diff --git a/android/res/drawable-hdpi/ic_graph_point.png b/android/res/drawable-hdpi/ic_graph_point.png new file mode 100755 index 0000000000..805bb4dafa Binary files /dev/null and b/android/res/drawable-hdpi/ic_graph_point.png differ diff --git a/android/res/drawable-hdpi/ic_you_marker.png b/android/res/drawable-hdpi/ic_you_marker.png new file mode 100755 index 0000000000..3dff257eca Binary files /dev/null and b/android/res/drawable-hdpi/ic_you_marker.png differ diff --git a/android/res/drawable-hdpi/trang.png b/android/res/drawable-hdpi/trang.png new file mode 100755 index 0000000000..ed8b04712d Binary files /dev/null and b/android/res/drawable-hdpi/trang.png differ diff --git a/android/res/drawable-mdpi/elevation.png b/android/res/drawable-mdpi/elevation.png new file mode 100755 index 0000000000..c020287038 Binary files /dev/null and b/android/res/drawable-mdpi/elevation.png differ diff --git a/android/res/drawable-mdpi/ic_chart_pin.png b/android/res/drawable-mdpi/ic_chart_pin.png new file mode 100755 index 0000000000..e9d323e672 Binary files /dev/null and b/android/res/drawable-mdpi/ic_chart_pin.png differ diff --git a/android/res/drawable-mdpi/ic_graph_point.png b/android/res/drawable-mdpi/ic_graph_point.png new file mode 100755 index 0000000000..839b2f0abc Binary files /dev/null and b/android/res/drawable-mdpi/ic_graph_point.png differ diff --git a/android/res/drawable-mdpi/ic_you_marker.png b/android/res/drawable-mdpi/ic_you_marker.png new file mode 100755 index 0000000000..0a6bd2b944 Binary files /dev/null and b/android/res/drawable-mdpi/ic_you_marker.png differ diff --git a/android/res/drawable-mdpi/trang.png b/android/res/drawable-mdpi/trang.png new file mode 100755 index 0000000000..5b96542af8 Binary files /dev/null and b/android/res/drawable-mdpi/trang.png differ diff --git a/android/res/drawable-xhdpi/elevation.png b/android/res/drawable-xhdpi/elevation.png new file mode 100755 index 0000000000..44990490c6 Binary files /dev/null and b/android/res/drawable-xhdpi/elevation.png differ diff --git a/android/res/drawable-xhdpi/ic_chart_pin.png b/android/res/drawable-xhdpi/ic_chart_pin.png new file mode 100755 index 0000000000..390857010b Binary files /dev/null and b/android/res/drawable-xhdpi/ic_chart_pin.png differ diff --git a/android/res/drawable-xhdpi/ic_graph_point.png b/android/res/drawable-xhdpi/ic_graph_point.png new file mode 100755 index 0000000000..0ec8a244e6 Binary files /dev/null and b/android/res/drawable-xhdpi/ic_graph_point.png differ diff --git a/android/res/drawable-xhdpi/ic_you_marker.png b/android/res/drawable-xhdpi/ic_you_marker.png new file mode 100755 index 0000000000..5f69b04b52 Binary files /dev/null and b/android/res/drawable-xhdpi/ic_you_marker.png differ diff --git a/android/res/drawable-xhdpi/trang.png b/android/res/drawable-xhdpi/trang.png new file mode 100755 index 0000000000..816f2ada08 Binary files /dev/null and b/android/res/drawable-xhdpi/trang.png differ diff --git a/android/res/drawable-xxhdpi/elevation.png b/android/res/drawable-xxhdpi/elevation.png new file mode 100755 index 0000000000..47d80694c1 Binary files /dev/null and b/android/res/drawable-xxhdpi/elevation.png differ diff --git a/android/res/drawable-xxhdpi/ic_chart_pin.png b/android/res/drawable-xxhdpi/ic_chart_pin.png new file mode 100755 index 0000000000..01cac2ac4d Binary files /dev/null and b/android/res/drawable-xxhdpi/ic_chart_pin.png differ diff --git a/android/res/drawable-xxhdpi/ic_graph_point.png b/android/res/drawable-xxhdpi/ic_graph_point.png new file mode 100755 index 0000000000..cd93ea4e0c Binary files /dev/null and b/android/res/drawable-xxhdpi/ic_graph_point.png differ diff --git a/android/res/drawable-xxhdpi/ic_you_marker.png b/android/res/drawable-xxhdpi/ic_you_marker.png new file mode 100755 index 0000000000..3aa7ad89de Binary files /dev/null and b/android/res/drawable-xxhdpi/ic_you_marker.png differ diff --git a/android/res/drawable-xxhdpi/trang.png b/android/res/drawable-xxhdpi/trang.png new file mode 100755 index 0000000000..c4ea8c87cf Binary files /dev/null and b/android/res/drawable-xxhdpi/trang.png differ diff --git a/android/res/drawable-xxxhdpi/elevation.png b/android/res/drawable-xxxhdpi/elevation.png new file mode 100755 index 0000000000..450cec3f65 Binary files /dev/null and b/android/res/drawable-xxxhdpi/elevation.png differ diff --git a/android/res/drawable-xxxhdpi/ic_chart_pin.png b/android/res/drawable-xxxhdpi/ic_chart_pin.png new file mode 100755 index 0000000000..ea1016b979 Binary files /dev/null and b/android/res/drawable-xxxhdpi/ic_chart_pin.png differ diff --git a/android/res/drawable-xxxhdpi/ic_graph_point.png b/android/res/drawable-xxxhdpi/ic_graph_point.png new file mode 100755 index 0000000000..e8f9b06786 Binary files /dev/null and b/android/res/drawable-xxxhdpi/ic_graph_point.png differ diff --git a/android/res/drawable-xxxhdpi/ic_you_marker.png b/android/res/drawable-xxxhdpi/ic_you_marker.png new file mode 100755 index 0000000000..ab9c5e062e Binary files /dev/null and b/android/res/drawable-xxxhdpi/ic_you_marker.png differ diff --git a/android/res/drawable-xxxhdpi/trang.png b/android/res/drawable-xxxhdpi/trang.png new file mode 100755 index 0000000000..7443bcec2b Binary files /dev/null and b/android/res/drawable-xxxhdpi/trang.png differ diff --git a/android/res/drawable/bg_altitude.xml b/android/res/drawable/bg_altitude.xml new file mode 100644 index 0000000000..1bf2d0663d --- /dev/null +++ b/android/res/drawable/bg_altitude.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/android/res/drawable/bg_your_location_pin.xml b/android/res/drawable/bg_your_location_pin.xml new file mode 100644 index 0000000000..4c4f078ef5 --- /dev/null +++ b/android/res/drawable/bg_your_location_pin.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/android/res/layout/altitude_chart.xml b/android/res/layout/altitude_chart.xml new file mode 100644 index 0000000000..f1784437b0 --- /dev/null +++ b/android/res/layout/altitude_chart.xml @@ -0,0 +1,52 @@ + + + + + + + + + + diff --git a/android/res/layout/current_location_marker.xml b/android/res/layout/current_location_marker.xml new file mode 100644 index 0000000000..6e21b2005f --- /dev/null +++ b/android/res/layout/current_location_marker.xml @@ -0,0 +1,32 @@ + + + + + + + diff --git a/android/res/layout/floating_marker_view.xml b/android/res/layout/floating_marker_view.xml new file mode 100644 index 0000000000..cd18973d37 --- /dev/null +++ b/android/res/layout/floating_marker_view.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + diff --git a/android/res/layout/place_page_details.xml b/android/res/layout/place_page_details.xml index 10465c97db..2984e1db5c 100644 --- a/android/res/layout/place_page_details.xml +++ b/android/res/layout/place_page_details.xml @@ -13,6 +13,8 @@ + + #FF4BB9E6 #FF1C85D6 #FF3C9BBE + #1E249CF2 #FFF54137 #F51E30 @@ -217,4 +218,5 @@ #DAF8FF #FF9600 + #1E96F0 diff --git a/android/res/values/dimens.xml b/android/res/values/dimens.xml index 4c33f3d2ff..cc0e7cc9f0 100644 --- a/android/res/values/dimens.xml +++ b/android/res/values/dimens.xml @@ -292,4 +292,6 @@ 328dp @dimen/margin_base_plus 88dp + 144dp + 72dp diff --git a/android/src/com/mapswithme/maps/widget/placepage/AxisValueFormatter.java b/android/src/com/mapswithme/maps/widget/placepage/AxisValueFormatter.java new file mode 100644 index 0000000000..9c7d08012a --- /dev/null +++ b/android/src/com/mapswithme/maps/widget/placepage/AxisValueFormatter.java @@ -0,0 +1,29 @@ +package com.mapswithme.maps.widget.placepage; + +import androidx.annotation.NonNull; +import com.github.mikephil.charting.formatter.DefaultValueFormatter; + +public class AxisValueFormatter extends DefaultValueFormatter +{ + private static final String DEF_DIMEN = "m"; + private static final int DEF_DIGITS = 1; + + @NonNull + private String mDimen = DEF_DIMEN; + + AxisValueFormatter() + { + super(DEF_DIGITS); + } + + @Override + public String getFormattedValue(float value) + { + return super.getFormattedValue(value) + mDimen; + } + + public void setDimen(@NonNull String dimen) + { + mDimen = dimen; + } +} diff --git a/android/src/com/mapswithme/maps/widget/placepage/CurrentLocationMarkerView.java b/android/src/com/mapswithme/maps/widget/placepage/CurrentLocationMarkerView.java new file mode 100644 index 0000000000..8830caacb3 --- /dev/null +++ b/android/src/com/mapswithme/maps/widget/placepage/CurrentLocationMarkerView.java @@ -0,0 +1,34 @@ +package com.mapswithme.maps.widget.placepage; + +import android.annotation.SuppressLint; +import android.content.Context; + +import androidx.annotation.NonNull; +import com.github.mikephil.charting.components.MarkerView; +import com.github.mikephil.charting.utils.MPPointF; +import com.mapswithme.maps.R; + +@SuppressLint("ViewConstructor") +class CurrentLocationMarkerView extends MarkerView +{ + /** + * Constructor. Sets up the MarkerView with a custom layout resource. + * + * @param context + */ + public CurrentLocationMarkerView(@NonNull Context context) + { + super(context, R.layout.current_location_marker); + } + + @Override + public MPPointF getOffset() { + return new MPPointF(-(getWidth() / 2f), -getHeight()); + } + + @Override + public MPPointF getOffsetForDrawingAtPoint(float posX, float posY) + { + return getOffset(); + } +} diff --git a/android/src/com/mapswithme/maps/widget/placepage/FloatingMarkerView.java b/android/src/com/mapswithme/maps/widget/placepage/FloatingMarkerView.java new file mode 100644 index 0000000000..048fc6c96e --- /dev/null +++ b/android/src/com/mapswithme/maps/widget/placepage/FloatingMarkerView.java @@ -0,0 +1,123 @@ + +package com.mapswithme.maps.widget.placepage; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.graphics.RectF; +import android.view.View; +import android.widget.RelativeLayout; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import com.github.mikephil.charting.components.MarkerView; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.utils.MPPointF; +import com.mapswithme.maps.R; + +@SuppressLint("ViewConstructor") +public class FloatingMarkerView extends MarkerView +{ + @NonNull + private final View mImage; + @NonNull + private final View mTextContainer; + @NonNull + private final View mSlidingContainer; + @NonNull + private final TextView mAltitudeView; + @NonNull + private final TextView mDistanceView; + private float mOffset; + + public FloatingMarkerView(@NonNull Context context) + { + super(context, R.layout.floating_marker_view); + mTextContainer = findViewById(R.id.text_container); + mImage = findViewById(R.id.image); + mSlidingContainer = findViewById(R.id.sliding_container); + mDistanceView = findViewById(R.id.distance_text); + mAltitudeView = findViewById(R.id.altitude); + } + + // runs every time the MarkerView is redrawn, can be used to update the + // content (user-interface) + @Override + public void refreshContent(Entry e, Highlight highlight) + { + updatePointValues(e); + + super.refreshContent(e, highlight); + } + + @Override + public MPPointF getOffset() + { + return new MPPointF(mOffset, -getHeight() / 2f); + } + + @Override + public MPPointF getOffsetForDrawingAtPoint(float posX, float posY) + { + return getOffset(); + } + + public void updateOffsets(Entry entry, Highlight highlight) + { + updateVertical(entry); + updateHorizontal(highlight); + } + + private void updateVertical(@NonNull Entry entry) + { + LayoutParams layoutParams = (LayoutParams) mTextContainer.getLayoutParams(); + float posY = entry.getY(); + if (posY + mSlidingContainer.getHeight() / 2f >= getChartView().getYChartMax()) + { + layoutParams.addRule(ALIGN_PARENT_BOTTOM); + layoutParams.removeRule(ALIGN_PARENT_TOP); + layoutParams.removeRule(CENTER_VERTICAL); + } + else if (posY - mSlidingContainer.getHeight() / 2f <= getChartView().getYChartMin()) + { + layoutParams.addRule(ALIGN_PARENT_TOP); + layoutParams.removeRule(ALIGN_PARENT_BOTTOM); + layoutParams.removeRule(CENTER_VERTICAL); + } + else + { + layoutParams.addRule(CENTER_VERTICAL); + layoutParams.removeRule(ALIGN_PARENT_TOP); + layoutParams.removeRule(ALIGN_PARENT_BOTTOM); + } + mTextContainer.setLayoutParams(layoutParams); + } + + private void updatePointValues(@NonNull Entry entry) + { + mDistanceView.setText(String.format("Distance : %s km", entry.getX())); + mAltitudeView.setText(String.format("%sm", entry.getY())); + } + + private void updateHorizontal(@NonNull Highlight highlight) + { + final float halfImg = Math.abs(mImage.getWidth()) / 2f; + final int wholeText = Math.abs(mTextContainer.getWidth()); + RectF rect = getChartView().getContentRect(); + + LayoutParams textParams =(LayoutParams) mTextContainer.getLayoutParams(); + LayoutParams imgParams = (LayoutParams) mImage.getLayoutParams(); + + boolean isLeftToRightDirection = highlight.getXPx() + halfImg + wholeText >= rect.right; + mOffset = isLeftToRightDirection ? -getWidth() + halfImg : -halfImg; + int anchorId = isLeftToRightDirection ? R.id.text_container : R.id.image; + LayoutParams toBecomeAnchor = isLeftToRightDirection ? textParams : imgParams; + LayoutParams toBecomeDependent = isLeftToRightDirection ? imgParams : textParams; + + toBecomeAnchor.removeRule(RelativeLayout.END_OF); + toBecomeDependent.addRule(RelativeLayout.END_OF, anchorId); + + mTextContainer.setLayoutParams(textParams); + mImage.setLayoutParams(imgParams); + } +} diff --git a/android/src/com/mapswithme/maps/widget/placepage/PlacePageView.java b/android/src/com/mapswithme/maps/widget/placepage/PlacePageView.java index 88e98056a1..bb1a89aa50 100644 --- a/android/src/com/mapswithme/maps/widget/placepage/PlacePageView.java +++ b/android/src/com/mapswithme/maps/widget/placepage/PlacePageView.java @@ -39,6 +39,17 @@ import androidx.fragment.app.Fragment; import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; +import com.github.mikephil.charting.charts.LineChart; +import com.github.mikephil.charting.components.Legend; +import com.github.mikephil.charting.components.MarkerView; +import com.github.mikephil.charting.components.XAxis; +import com.github.mikephil.charting.components.YAxis; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.data.LineData; +import com.github.mikephil.charting.data.LineDataSet; +import com.github.mikephil.charting.formatter.ValueFormatter; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.listener.OnChartValueSelectedListener; import com.mapswithme.maps.Framework; import com.mapswithme.maps.MwmActivity; import com.mapswithme.maps.MwmApplication; @@ -122,7 +133,7 @@ public class PlacePageView extends NestedScrollView RecyclerClickListener, NearbyAdapter.OnItemClickListener, EditBookmarkFragment.EditBookmarkListener, - Detachable + Detachable, OnChartValueSelectedListener { private static final Logger LOGGER = LoggerFactory.INSTANCE.getLogger(LoggerFactory.Type.MISC); @@ -130,6 +141,11 @@ public class PlacePageView extends NestedScrollView private static final String PREF_USE_DMS = "use_dms"; private static final String DISCOUNT_PREFIX = "-"; private static final String DISCOUNT_SUFFIX = "%"; + private static final int CHART_Y_LABEL_COUNT = 3; + private static final int CHART_X_LABEL_COUNT = 6; + private static final int CHART_ANIMATION_DURATION = 1500; + private static final int CHART_FILL_ALPHA = (int) (0.12 * 255); + private static final float CUBIC_INTENSITY = 0.2f; private boolean mIsDocked; private boolean mIsFloating; @@ -327,6 +343,18 @@ public class PlacePageView extends NestedScrollView @NonNull private View mCatalogPromoTitleView; + @SuppressWarnings("NullableProblems") + @NonNull + private LineChart mChart; + + @SuppressWarnings("NullableProblems") + @NonNull + private FloatingMarkerView mFloatingMarkerView; + + @SuppressWarnings("NullableProblems") + @NonNull + private MarkerView mCurrentLocationMarkerView; + void setScrollable(boolean scrollable) { mScrollable = scrollable; @@ -367,6 +395,30 @@ public class PlacePageView extends NestedScrollView mCatalogPromoController.detach(); } + @Override + public void onValueSelected(Entry e, Highlight h) + { + mFloatingMarkerView.updateOffsets(e, h); + Highlight curPos = getCurrentPosHighlight(); + + mChart.highlightValues(Arrays.asList(curPos, h), + Arrays.asList(mCurrentLocationMarkerView, mFloatingMarkerView)); + } + + @NonNull + private Highlight getCurrentPosHighlight() + { + LineData data = mChart.getData(); + final Entry entryForIndex = data.getDataSetByIndex(0).getEntryForIndex(5); + return new Highlight(entryForIndex.getX(), 0, 5); + } + + @Override + public void onNothingSelected() + { + highlightChartCurrentLocation(); + } + public interface SetMapObjectListener { void onSetMapObjectComplete(@NonNull NetworkPolicy policy, boolean isSameObject); @@ -497,6 +549,111 @@ public class PlacePageView extends NestedScrollView Sponsored.setInfoListener(this); initPlaceDescriptionView(); + + initChart(); + } + + private void initChart() + { + TextView topAlt = findViewById(R.id.highest_altitude); + topAlt.setText("10000m"); + TextView bottomAlt = findViewById(R.id.lowest_altitude); + bottomAlt.setText("100m"); + + + mChart = findViewById(R.id.elevation_profile_chart); + mChart.setBackgroundColor(Color.WHITE); + mChart.setTouchEnabled(true); + mChart.setOnChartValueSelectedListener(this); + mChart.setDrawGridBackground(false); + mChart.setDragEnabled(true); + mChart.setScaleEnabled(true); + mChart.setPinchZoom(true); + int sideOffset = getResources().getDimensionPixelSize(R.dimen.margin_base); + mChart.setExtraTopOffset(0); + int topOffset = getResources().getDimensionPixelSize(R.dimen.margin_base_plus) / 2; + mChart.setViewPortOffsets(sideOffset, topOffset, sideOffset, getResources().getDimensionPixelSize(R.dimen.margin_base_plus_quarter)); + mChart.getDescription().setEnabled(false); + mChart.setDrawBorders(false); + Legend l = mChart.getLegend(); + l.setEnabled(false); + initAxises(); + setData(20, 180); + + mFloatingMarkerView = new FloatingMarkerView(getActivity()); + mCurrentLocationMarkerView = new CurrentLocationMarkerView(getActivity()); + mFloatingMarkerView.setChartView(mChart); + mCurrentLocationMarkerView.setChartView(mChart); + highlightChartCurrentLocation(); + mChart.animateX(CHART_ANIMATION_DURATION); + } + + private void highlightChartCurrentLocation() + { + mChart.highlightValues(Collections.singletonList(getCurrentPosHighlight()), + Collections.singletonList(mCurrentLocationMarkerView)); + } + + private void initAxises() + { + XAxis x = mChart.getXAxis(); + x.setLabelCount(CHART_X_LABEL_COUNT, false); + x.setAvoidFirstLastClipping(true); + x.setDrawGridLines(false); + x.setTextColor(getResources().getColor(R.color.black_50)); + x.setPosition(XAxis.XAxisPosition.BOTTOM); + ValueFormatter xAxisFormatter = new AxisValueFormatter(); + x.setValueFormatter(xAxisFormatter); + + YAxis y = mChart.getAxisLeft(); + y.setLabelCount(CHART_Y_LABEL_COUNT, false); + y.setPosition(YAxis.YAxisLabelPosition.INSIDE_CHART); + y.setDrawGridLines(true); + y.setGridColor(getResources().getColor(R.color.black_12)); + y.setEnabled(true); + y.setTextColor(Color.TRANSPARENT); + y.setAxisLineColor(Color.WHITE); + int lineLength = getResources().getDimensionPixelSize(R.dimen.margin_eighth); + y.enableGridDashedLine(lineLength, 2 * lineLength, 0); + + mChart.getAxisRight().setEnabled(false); + } + + private void setData(int count, float range) + { + List values = new ArrayList<>(); + + for (int i = 0; i < count; i++) + { + float val = (float) (Math.random() * (range + 1)) + 20; + values.add(new Entry(i, val)); + } + + LineDataSet set; + + // create a dataset and give it a type + set = new LineDataSet(values, "DataSet 1"); + + set.setMode(LineDataSet.Mode.CUBIC_BEZIER); + set.setCubicIntensity(CUBIC_INTENSITY); + set.setDrawFilled(true); + set.setDrawCircles(false); + int lineThickness = getResources().getDimensionPixelSize(R.dimen.divider_width); + set.setLineWidth(lineThickness); + set.setCircleColor(getResources().getColor(R.color.base_accent)); + set.setColor(getResources().getColor(R.color.base_accent)); + set.setFillAlpha(CHART_FILL_ALPHA); + set.setDrawHorizontalHighlightIndicator(false); + set.setHighlightLineWidth(lineThickness); + set.setHighLightColor(getResources().getColor(R.color.base_accent_transparent)); + set.setFillColor(getResources().getColor(R.color.chart_color)); + + + LineData data = new LineData(set); + data.setValueTextSize(getResources().getDimensionPixelSize(R.dimen.text_size_icon_title)); + data.setDrawValues(false); + + mChart.setData(data); } public void initButtons(@NonNull ViewGroup buttons) diff --git a/data/copyright.html b/data/copyright.html index dc11dffb58..3b18985c78 100644 --- a/data/copyright.html +++ b/data/copyright.html @@ -212,6 +212,9 @@
  • Material Tap Target Prompt
    © 2016-2018 Samuel Wall; Apache License
  • +
  • MPAndroidChart
    + © 2020 Philipp Jahoda; Apache License
  • +
  • Linear Layout Manager
    © 2014 serso aka se.solovyev; Apache License