From 37974f2ce8f1595124200246ec0a1d63e352abdd Mon Sep 17 00:00:00 2001 From: cyBerta Date: Tue, 26 Mar 2019 16:05:11 +0100 Subject: [PATCH] implement filtering, move time label above the slider, minor performance optimizations --- res/layout/bottom_map_sheet.xml | 5 +- res/values/dimens.xml | 2 +- .../rangeslider/RangeSliderView.java | 33 +++++---- .../rangeslider/TimeRangeSlider.java | 32 +++++++-- .../securesms/map/GenerateInfoWindowTask.java | 2 +- .../securesms/map/MapActivity.java | 27 +++---- .../securesms/map/MapDataManager.java | 71 ++++++++++++++++--- 7 files changed, 122 insertions(+), 50 deletions(-) diff --git a/res/layout/bottom_map_sheet.xml b/res/layout/bottom_map_sheet.xml index dc129de1d..c3ffe21b4 100644 --- a/res/layout/bottom_map_sheet.xml +++ b/res/layout/bottom_map_sheet.xml @@ -3,7 +3,7 @@ xmlns:app="http://schemas.android.com/apk/res-auto" android:orientation="vertical" android:layout_width="match_parent" - android:layout_height="150dp" + android:layout_height="165dp" android:id="@+id/bottom_sheet" app:behavior_hideable="true" app:behavior_peekHeight="35dp" @@ -50,12 +50,13 @@ android:gravity="center" android:text="@string/filter_map_on_time" android:paddingTop="8dp" + android:paddingBottom="8dp" android:background="@color/white" android:textColor="@android:color/black" /> 14dp 4dp 12sp - 6dp + 8dp 100 100 diff --git a/src/org/thoughtcrime/securesms/components/rangeslider/RangeSliderView.java b/src/org/thoughtcrime/securesms/components/rangeslider/RangeSliderView.java index 8e361e55b..ccd5d591e 100644 --- a/src/org/thoughtcrime/securesms/components/rangeslider/RangeSliderView.java +++ b/src/org/thoughtcrime/securesms/components/rangeslider/RangeSliderView.java @@ -43,6 +43,11 @@ public class RangeSliderView extends View { protected int maxValue; protected float delta; + protected float offsetY; + protected float maxValueDisplayLabelOffsetY; + protected float minValueDisplayLabelOffsetY; + protected float trackOffsetY; + private float beginTrackOffsetX; private boolean isThumbViewLocked; @@ -159,19 +164,22 @@ public class RangeSliderView extends View { } + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + super.onSizeChanged(w, h, oldw, oldh); + this.offsetY = getHeight() / 5 * 3; + maxValueDisplayLabelOffsetY = offsetY - thumbRadius - thumbOutlineSize - trackHeight / 2 - displayTextBasicOffsetY - displayTextFontSize; + minValueDisplayLabelOffsetY = maxValueDisplayLabelOffsetY + displayTextFontSize + displayTextFontSize / 2; + trackOffsetY = offsetY - trackHeight / 2; + } + @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); - float offsetY = getHeight() / 2; float minValueOffsetX = getPositionFromIndex(minValue); float maxValueOffsetX = getPositionFromIndex(maxValue); - float displayLabelOffsetAbove = offsetY - thumbRadius - displayTextBasicOffsetY; - float displayLabelOffsetBelow = offsetY + thumbRadius + thumbOutlineSize + displayTextFontSize; - - Log.d(TAG, "onDraw minValueOffsetX: " + minValueOffsetX + ", maxValueOffsetX: " + maxValueOffsetX); - track.draw(canvas, getWidth(), minValueOffsetX, maxValueOffsetX, offsetY - trackHeight / 2, getDeltaInPixel()); - + track.draw(canvas, getWidth(), minValueOffsetX, maxValueOffsetX, trackOffsetY, getDeltaInPixel()); minValueThumb.draw(canvas, minValueOffsetX, offsetY); maxValueThumb.draw(canvas, maxValueOffsetX, offsetY); @@ -180,19 +188,17 @@ public class RangeSliderView extends View { canvas, onValueChangedListener.parseMinValueDisplayText(minValue), minValueOffsetX, - displayLabelOffsetBelow + minValueDisplayLabelOffsetY ); maxValueDisplayLabel.draw( canvas, onValueChangedListener.parseMaxValueDisplayText(maxValue), maxValueOffsetX, - displayLabelOffsetAbove + maxValueDisplayLabelOffsetY ); - - } else { - minValueDisplayLabel.draw(canvas, String.valueOf(minValue), minValueOffsetX, displayLabelOffsetBelow); - maxValueDisplayLabel.draw(canvas, String.valueOf(maxValue), maxValueOffsetX, displayLabelOffsetAbove); + minValueDisplayLabel.draw(canvas, String.valueOf(minValue), minValueOffsetX, minValueDisplayLabelOffsetY); + maxValueDisplayLabel.draw(canvas, String.valueOf(maxValue), maxValueOffsetX, maxValueDisplayLabelOffsetY); } } @@ -208,7 +214,6 @@ public class RangeSliderView extends View { return true; } - if (event.getAction() == MotionEvent.ACTION_UP) { isThumbViewLocked = false; if (minValueThumb.isHighlight || maxValueThumb.isHighlight) { diff --git a/src/org/thoughtcrime/securesms/components/rangeslider/TimeRangeSlider.java b/src/org/thoughtcrime/securesms/components/rangeslider/TimeRangeSlider.java index 4a27b0429..cd2896af5 100644 --- a/src/org/thoughtcrime/securesms/components/rangeslider/TimeRangeSlider.java +++ b/src/org/thoughtcrime/securesms/components/rangeslider/TimeRangeSlider.java @@ -25,6 +25,8 @@ public class TimeRangeSlider extends RangeSliderView implements RangeSliderView. private TextLayer minRangeDisplayLabel; private TextLayer maxRangeDisplayLabel; + float displayLabelOffsetBelow; + public TimeRangeSlider(Context context) { this(context, null, 0); @@ -50,14 +52,18 @@ public class TimeRangeSlider extends RangeSliderView implements RangeSliderView. delta = DEFAULT_DELTA / (timeFrame * 1000) * getCount(); } + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + super.onSizeChanged(w, h, oldw, oldh); + displayLabelOffsetBelow = offsetY + thumbRadius + thumbOutlineSize + trackHeight / 2 + displayTextBasicOffsetY; + } + @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); - float offsetY = getHeight() / 2; float minValueOffsetX = getPositionFromIndex(minValue); float maxValueOffsetX = getPositionFromIndex(maxValue); - float displayLabelOffsetBelow = offsetY + thumbRadius + thumbOutlineSize + displayTextFontSize; if (minValue == maxValue) { if (minValueOffsetX - getDeltaInPixel() >= sliderPaddingLeft @@ -83,7 +89,13 @@ public class TimeRangeSlider extends RangeSliderView implements RangeSliderView. } public interface OnTimestampChangedListener { - void onValueChanged(long startTimestamp, long stopTimestamp); + void onTimestampChanged(long startTimestamp, long stopTimestamp); + + /** + * filter for lastPosition beginning from startTimestamp to now + * @param startTimestamp begin of time frame + */ + void onFilterLastPosition(long startTimestamp); } @@ -94,7 +106,19 @@ public class TimeRangeSlider extends RangeSliderView implements RangeSliderView. @Override public void onValueChanged(int minValue, int maxValue) { if (listener != null) { - listener.onValueChanged(getTimestampForValue(minValue), getTimestampForValue(maxValue)); + long minTimeStamp = getTimestampForValue(minValue); + if (minValue == maxValue) { + if (minValue == getCount()) { + // filter last location + listener.onFilterLastPosition(System.currentTimeMillis() - (long) DEFAULT_DELTA); + } else { + // filter for time of event with delta before and after + listener.onTimestampChanged(minTimeStamp - (long) DEFAULT_DELTA, minTimeStamp + (long) DEFAULT_DELTA); + } + } else { + //filter for time span + listener.onTimestampChanged(minTimeStamp, getTimestampForValue(maxValue)); + } } } diff --git a/src/org/thoughtcrime/securesms/map/GenerateInfoWindowTask.java b/src/org/thoughtcrime/securesms/map/GenerateInfoWindowTask.java index 6f3699d9c..6d584690b 100644 --- a/src/org/thoughtcrime/securesms/map/GenerateInfoWindowTask.java +++ b/src/org/thoughtcrime/securesms/map/GenerateInfoWindowTask.java @@ -116,7 +116,7 @@ public class GenerateInfoWindowTask extends AsyncTask, HashMa String id = feature.getStringProperty(INFO_WINDOW_ID); imagesMap.put(id, bitmap); - if (i % 10 == 0) { + if (i % 20 == 0) { publishProgress(new HashMap<>(imagesMap)); imagesMap.clear(); } diff --git a/src/org/thoughtcrime/securesms/map/MapActivity.java b/src/org/thoughtcrime/securesms/map/MapActivity.java index c3396f76b..b562be297 100644 --- a/src/org/thoughtcrime/securesms/map/MapActivity.java +++ b/src/org/thoughtcrime/securesms/map/MapActivity.java @@ -3,8 +3,6 @@ package org.thoughtcrime.securesms.map; import android.content.Intent; import android.graphics.PointF; import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; import android.support.design.widget.BottomSheetBehavior; import android.support.v4.app.FragmentTransaction; import android.support.v7.app.AlertDialog; @@ -15,11 +13,8 @@ import android.widget.RelativeLayout; import com.b44t.messenger.DcMsg; import com.mapbox.geojson.Feature; import com.mapbox.mapboxsdk.camera.CameraPosition; -import com.mapbox.mapboxsdk.camera.CameraUpdate; import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; -import com.mapbox.mapboxsdk.exceptions.InvalidLatLngBoundsException; import com.mapbox.mapboxsdk.geometry.LatLng; -import com.mapbox.mapboxsdk.geometry.LatLngBounds; import com.mapbox.mapboxsdk.maps.MapboxMap; import com.mapbox.mapboxsdk.maps.Style; import com.mapbox.mapboxsdk.maps.SupportMapFragment; @@ -39,7 +34,6 @@ import java.util.Observer; import static android.support.design.widget.BottomSheetBehavior.STATE_COLLAPSED; import static android.support.design.widget.BottomSheetBehavior.STATE_EXPANDED; -import static android.support.design.widget.BottomSheetBehavior.STATE_HIDDEN; import static com.b44t.messenger.DcChat.DC_CHAT_NO_CHAT; import static org.thoughtcrime.securesms.map.MapDataManager.MARKER_SELECTED; import static org.thoughtcrime.securesms.map.MapDataManager.MESSAGE_ID; @@ -95,6 +89,7 @@ public class MapActivity extends BaseActivity implements Observer, TimeRangeSlid } mapDataManager = new MapDataManager(this, mapBoxStyle, chatId, (latLngBounds) -> { + Log.d(TAG, "on Data initialized"); mapboxMap.easeCamera(CameraUpdateFactory.newLatLngBounds(latLngBounds, 50), 1000); }); @@ -145,7 +140,7 @@ public class MapActivity extends BaseActivity implements Observer, TimeRangeSlid Log.e(TAG, "Chat id is 0. Cannot open chat"); return true; } - + Intent intent = new Intent(MapActivity.this, ConversationActivity.class); intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, chatId); intent.putExtra(ConversationActivity.LAST_SEEN_EXTRA, 0); @@ -222,18 +217,16 @@ public class MapActivity extends BaseActivity implements Observer, TimeRangeSlid } @Override - public void onValueChanged(long startTimestamp, long stopTimestamp) { + public void onTimestampChanged(long startTimestamp, long stopTimestamp) { if (this.mapboxMap == null) { return; } - LatLngBounds.Builder boundingBuilder = new LatLngBounds.Builder(); - mapDataManager.filter(startTimestamp, stopTimestamp, boundingBuilder); - try { - mapboxMap.easeCamera( - CameraUpdateFactory.newLatLngBounds(boundingBuilder.build(), 50, 50, 50, 200), - 500); - } catch (InvalidLatLngBoundsException e) { - e.printStackTrace(); - } + mapDataManager.filter(startTimestamp, stopTimestamp); + + } + + @Override + public void onFilterLastPosition(long startTimestamp) { + mapDataManager.filterLastPositions(startTimestamp); } } diff --git a/src/org/thoughtcrime/securesms/map/MapDataManager.java b/src/org/thoughtcrime/securesms/map/MapDataManager.java index 2a7596725..bdbedfb1b 100644 --- a/src/org/thoughtcrime/securesms/map/MapDataManager.java +++ b/src/org/thoughtcrime/securesms/map/MapDataManager.java @@ -46,9 +46,13 @@ import java.util.Map; import static com.b44t.messenger.DcContext.DC_EVENT_LOCATION_CHANGED; import static com.b44t.messenger.DcContext.DC_GCL_ADD_SELF; +import static com.mapbox.mapboxsdk.style.expressions.Expression.all; import static com.mapbox.mapboxsdk.style.expressions.Expression.eq; import static com.mapbox.mapboxsdk.style.expressions.Expression.get; +import static com.mapbox.mapboxsdk.style.expressions.Expression.gt; +import static com.mapbox.mapboxsdk.style.expressions.Expression.gte; import static com.mapbox.mapboxsdk.style.expressions.Expression.literal; +import static com.mapbox.mapboxsdk.style.expressions.Expression.lte; import static com.mapbox.mapboxsdk.style.expressions.Expression.switchCase; import static com.mapbox.mapboxsdk.style.expressions.Expression.toBool; import static com.mapbox.mapboxsdk.style.layers.Property.ICON_ANCHOR_BOTTOM_LEFT; @@ -73,6 +77,8 @@ public class MapDataManager implements DcEventCenter.DcEventDelegate, GenerateIn public static final int ALL_CHATS_GLOBAL_MAP = 0; public static final long TIMESTAMP_NOW = 0L; public static final long TIMEOUT = 3 * 60 * 60 * 1000; + private static final long DEFAULT_LAST_POSITION_DELTA = 1000*60*30; // 30 min + private static final String TAG = MapDataManager.class.getSimpleName(); private Style mapboxStyle; private HashMap contactMapSources; @@ -98,9 +104,11 @@ public class MapDataManager implements DcEventCenter.DcEventDelegate, GenerateIn int[] contactIds = getContactIds(chatId); + long now = System.currentTimeMillis(); for (int contactId : contactIds) { updateSource(chatId, contactId, boundingBuilder); generateInfoWindows(contactId); + filterLastPosition(contactId, now - DEFAULT_LAST_POSITION_DELTA); } dcContext.eventCenter.addObserver(DC_EVENT_LOCATION_CHANGED, this); @@ -204,14 +212,39 @@ public class MapDataManager implements DcEventCenter.DcEventDelegate, GenerateIn } } - public void filter(long startTimestamp, long endTimestamp, LatLngBounds.Builder builder) { + + public void filter(long startTimestamp, long endTimestamp) { int[] contactIds = getContactIds(chatId); for (int contactId : contactIds) { - resetSource(chatId); - updateSource(chatId, contactId, startTimestamp, endTimestamp, builder); + MapSource contactMapMetadata = contactMapSources.get(contactId); + SymbolLayer markerLayer = (SymbolLayer) mapboxStyle.getLayer(contactMapMetadata.getMarkerLayer()); + Expression filter = all( + lte(get(TIMESTAMP), endTimestamp), + gte(get(TIMESTAMP), startTimestamp)); + markerLayer.withFilter(filter); + LineLayer lineLayer = (LineLayer) mapboxStyle.getLayer(contactMapMetadata.getLineLayer()); + lineLayer.withFilter(filter); } } + public void filterLastPositions(long startTimestamp) { + int[] contactIds = getContactIds(chatId); + for (int contactId : contactIds) { + filterLastPosition(contactId, startTimestamp); + } + } + + private void filterLastPosition(int contactId, long startTimestamp) { + MapSource contactMapMetadata = contactMapSources.get(contactId); + SymbolLayer markerLayer = (SymbolLayer) mapboxStyle.getLayer(contactMapMetadata.getMarkerLayer()); + Expression filter = all( + eq((get(LAST_LOCATION)), literal(true)), + gte(get(TIMESTAMP), startTimestamp)); + markerLayer.withFilter(filter); + LineLayer lineLayer = (LineLayer) mapboxStyle.getLayer(contactMapMetadata.getLineLayer()); + lineLayer.withFilter(filter); + } + private void updateSource(int chatId, int contactId) { updateSource(chatId, contactId, null); } @@ -221,6 +254,7 @@ public class MapDataManager implements DcEventCenter.DcEventDelegate, GenerateIn } private void updateSource(int chatId, int contactId, long startTimestamp, long endTimestamp, LatLngBounds.Builder boundingBuilder) { + //long start = System.currentTimeMillis(); DcArray locations = dcContext.getLocations(chatId, contactId, startTimestamp, endTimestamp); MapSource contactMapMetadata = contactMapSources.get(contactId); if (contactMapMetadata == null) { @@ -254,10 +288,21 @@ public class MapDataManager implements DcEventCenter.DcEventDelegate, GenerateIn sortedPointFeatures.first().getFeature().addBooleanProperty(LAST_LOCATION, true); } - FeatureCollection pointFeatureCollection = FeatureCollection.fromFeatures(sortedPointFeatures.getFeatureList()); - FeatureCollection lineFeatureCollection = FeatureCollection.fromFeatures(new Feature[] {Feature.fromGeometry( - LineString.fromLngLats(sortedPointFeatures.getPointList()) - )}); + ArrayList sortedPointList = sortedPointFeatures.getPointList(); + ArrayList sortedFeatureList = sortedPointFeatures.getFeatureList(); + FeatureCollection pointFeatureCollection = FeatureCollection.fromFeatures(sortedFeatureList); + ArrayList lineFeatures = new ArrayList<>(); + + for (int i = 0; i < sortedPointFeatures.size() - 1; i++) { + Feature f = sortedFeatureList.get(i); + Number numberProperty = f.getNumberProperty(TIMESTAMP); + LineString l = LineString.fromLngLats(sortedPointList.subList(i, i+2)); + Feature lineFeature = Feature.fromGeometry(l, new JsonObject(), "l_" + f.id()); + lineFeature.addNumberProperty(TIMESTAMP, numberProperty); + lineFeatures.add(lineFeature); + } + + FeatureCollection lineFeatureCollection = FeatureCollection.fromFeatures(lineFeatures); GeoJsonSource lineSource = (GeoJsonSource) mapboxStyle.getSource(contactMapMetadata.getLineSource()); lineSource.setGeoJson(lineFeatureCollection); @@ -266,6 +311,8 @@ public class MapDataManager implements DcEventCenter.DcEventDelegate, GenerateIn featureCollections.put(contactMapMetadata.getMarkerFeatureCollection(), sortedPointFeatures); generateMissingInfoWindows(contactId); + + //Log.d(TAG, "update Source took " + (System.currentTimeMillis() - start) + " ms"); } private void generateMissingInfoWindows(int contactId) { @@ -293,7 +340,9 @@ public class MapDataManager implements DcEventCenter.DcEventDelegate, GenerateIn private void generateInfoWindows(int contactId) { MapSource contactMapMetadata = contactMapSources.get(contactId); FeatureTreeSet collection = featureCollections.get(contactMapMetadata.getMarkerFeatureCollection()); - new GenerateInfoWindowTask(this, contactId).execute(collection.getFeatureList()); + if (collection.size() > 0) { + new GenerateInfoWindowTask(this, contactId).execute(collection.getFeatureList()); + } } private void initGeoJsonSources(MapSource source) { @@ -340,12 +389,10 @@ public class MapDataManager implements DcEventCenter.DcEventDelegate, GenerateIn switchCase(toBool(get(LAST_LOCATION)), literal(1.0f), switchCase(eq(get(MESSAGE_ID), literal(0)), literal(0.7f), literal(1.1f)))); Expression markerIcon = switchCase(toBool(get(LAST_LOCATION)), literal(source.getMarkerLastPositon()), literal(source.getMarkerIcon())); - Expression allowOverlap = eq(get(LAST_LOCATION), literal(true)); mapboxStyle.addLayer(new SymbolLayer(source.getMarkerLayer(), source.getMarkerSource()) .withProperties( iconImage(markerIcon), - iconSize(markerSize), - iconAllowOverlap(allowOverlap)) + iconSize(markerSize)) ); mapboxStyle.addLayer(new LineLayer(source.getLineLayer(), source.getLineSource()) @@ -421,6 +468,8 @@ public class MapDataManager implements DcEventCenter.DcEventDelegate, GenerateIn MapSource contactMapMetadata = contactMapSources.get(contactId); if (contactMapMetadata != null) { featureCollections.put(contactMapMetadata.getMarkerFeatureCollection(), new FeatureTreeSet()); + GeoJsonSource pointSource = (GeoJsonSource) mapboxStyle.getSource(contactMapMetadata.getMarkerSource()); + pointSource.setGeoJson(FeatureCollection.fromFeatures(new ArrayList<>())); } }