diff --git a/res/drawable/ic_location_dot.xml b/res/drawable/ic_location_dot.xml
new file mode 100644
index 000000000..15c532a57
--- /dev/null
+++ b/res/drawable/ic_location_dot.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/src/org/thoughtcrime/securesms/ConversationActivity.java b/src/org/thoughtcrime/securesms/ConversationActivity.java
index c59791b55..13d8a4106 100644
--- a/src/org/thoughtcrime/securesms/ConversationActivity.java
+++ b/src/org/thoughtcrime/securesms/ConversationActivity.java
@@ -177,7 +177,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private Recipient recipient;
private ApplicationDcContext dcContext;
private DcChat dcChat = new DcChat(0);
- private int chatId;
+ private int chatId;
private boolean archived;
private final boolean isSecureText = true;
private boolean isDefaultSms = true;
@@ -475,6 +475,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private void handleShowMap() {
Intent intent = new Intent(this, MapActivity.class);
+ intent.putExtra(MapActivity.CHAT_ID, chatId);
startActivity(intent);
}
diff --git a/src/org/thoughtcrime/securesms/components/AttachmentTypeSelector.java b/src/org/thoughtcrime/securesms/components/AttachmentTypeSelector.java
index 1b8691647..b70afb6be 100644
--- a/src/org/thoughtcrime/securesms/components/AttachmentTypeSelector.java
+++ b/src/org/thoughtcrime/securesms/components/AttachmentTypeSelector.java
@@ -11,6 +11,7 @@ import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.LoaderManager;
+import android.support.v4.content.ContextCompat;
import android.util.Pair;
import android.view.Gravity;
import android.view.LayoutInflater;
@@ -163,11 +164,7 @@ public class AttachmentTypeSelector extends PopupWindow {
resId = R.drawable.ic_location_on_white_24dp;
}
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- this.locationButton.setImageDrawable(context.getDrawable(resId));
- } else {
- this.locationButton.setImageDrawable(context.getResources().getDrawable(resId));
- }
+ this.locationButton.setImageDrawable(ContextCompat.getDrawable(context, resId));
}
private void animateButtonIn(View button, int delay) {
diff --git a/src/org/thoughtcrime/securesms/geolocation/DcLocation.java b/src/org/thoughtcrime/securesms/geolocation/DcLocation.java
index 73cbd044e..948969b99 100644
--- a/src/org/thoughtcrime/securesms/geolocation/DcLocation.java
+++ b/src/org/thoughtcrime/securesms/geolocation/DcLocation.java
@@ -14,7 +14,7 @@ public class DcLocation extends Observable {
private static DcLocation instance;
private DcLocation() {
- lastLocation = new Location("?");
+ lastLocation = getDefault();
}
public static DcLocation getInstance() {
@@ -36,8 +36,15 @@ public class DcLocation extends Observable {
}
void reset() {
- updateLocation(new Location("?"));
+ updateLocation(getDefault());
+
}
+ private Location getDefault() {
+ Location location = new Location("?");
+ location.setLatitude(52.52);
+ location.setLongitude(13.404);
+ return location;
+ }
}
diff --git a/src/org/thoughtcrime/securesms/map/MapActivity.java b/src/org/thoughtcrime/securesms/map/MapActivity.java
index 907f58db1..b77956d58 100644
--- a/src/org/thoughtcrime/securesms/map/MapActivity.java
+++ b/src/org/thoughtcrime/securesms/map/MapActivity.java
@@ -31,23 +31,30 @@ import java.util.Observer;
public class MapActivity extends BaseActivity implements Observer {
+ public static final String TAG = MapActivity.class.getSimpleName();
+ public static final String CHAT_ID = "chat_id";
+
public static final String LINE_LAYER = "line_layer";
public static final String LINE_SOURCE = "line_source";
public static final String MARKER_LAYER = "symbol_layer";
public static final String MARKER_ICON = "marker_icon_id";
public static final String MARKER_POSITION_SOURCE = "marker_position";
- private static final String TAG = MapActivity.class.getSimpleName();
private MapView mapView;
private MapboxMap mapboxMap;
- private PermissionsManager permissionsManager;
private DcLocation dcLocation;
+ private MapDataManager mapDataManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_map);
+ int chatId = getIntent().getIntExtra(CHAT_ID, -1);
+ if (chatId == -1) {
+ finish();
+ return;
+ }
mapView = findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);
@@ -61,8 +68,12 @@ public class MapActivity extends BaseActivity implements Observer {
mapboxMap.getUiSettings().setLogoEnabled(false);
mapboxMap.getUiSettings().setAttributionEnabled(false);
- initMapDrawings();
- showDeviceLocation();
+ Style mapBoxStyle = mapboxMap.getStyle();
+ if (mapBoxStyle == null) {
+ return;
+ }
+
+ mapDataManager = new MapDataManager(this, mapBoxStyle, chatId);
}));
dcLocation = DcLocation.getInstance();
@@ -80,6 +91,9 @@ public class MapActivity extends BaseActivity implements Observer {
super.onResume();
mapView.onResume();
DcLocation.getInstance().addObserver(this);
+ if (mapDataManager != null) {
+ mapDataManager.onResume();
+ }
}
@Override
@@ -87,6 +101,9 @@ public class MapActivity extends BaseActivity implements Observer {
super.onPause();
mapView.onPause();
DcLocation.getInstance().deleteObserver(this);
+ if (mapDataManager != null) {
+ mapDataManager.onPause();
+ }
}
@Override
@@ -107,12 +124,6 @@ public class MapActivity extends BaseActivity implements Observer {
mapView.onSaveInstanceState(outState);
}
- //Android SDK callback for the result from requesting permissions
- @Override
- public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
- permissionsManager.onRequestPermissionsResult(requestCode, permissions, grantResults);
- }
-
private void initMapDrawings() {
if (mapboxMap == null || mapboxMap.getStyle() == null) {
return;
@@ -144,45 +155,6 @@ public class MapActivity extends BaseActivity implements Observer {
PropertyFactory.lineColor(Color.parseColor("#3bb2d0"))));
}
- private void showDeviceLocation() {
- if (this.dcLocation.getLastLocation().getProvider().equals("?")) {
- return;
- }
-
- FeatureCollection featureCollection = FeatureCollection.fromFeature(Feature.fromGeometry(
- Point.fromLngLat(dcLocation.getLastLocation().getLongitude(),
- dcLocation.getLastLocation().getLatitude())));
-
- drawPoints(featureCollection);
- }
-
- private void drawPoints(@NonNull FeatureCollection featureCollection) {
- if (mapboxMap == null ||
- mapboxMap.getStyle() == null ||
- featureCollection.features() == null ||
- featureCollection.features().size() == 0) {
- return;
- }
-
- Style style = mapboxMap.getStyle();
- GeoJsonSource source = (GeoJsonSource) style.getSource(MARKER_POSITION_SOURCE);
- source.setGeoJson(featureCollection);
- }
-
- private void drawLines(@NonNull FeatureCollection featureCollection) {
- if (mapboxMap == null ||
- mapboxMap.getStyle() == null ||
- featureCollection.features() == null ||
- featureCollection.features().size() == 0) {
- return;
- }
-
- Style style = mapboxMap.getStyle();
- GeoJsonSource source = (GeoJsonSource) style.getSource(LINE_SOURCE);
- source.setGeoJson(featureCollection);
- }
-
-
@Override
public void update(Observable o, Object arg) {
if (o instanceof DcLocation) {
@@ -190,8 +162,7 @@ public class MapActivity extends BaseActivity implements Observer {
Log.d(TAG, "show marker on map: " +
dcLocation.getLastLocation().getLatitude() + ", " +
dcLocation.getLastLocation().getLongitude());
- showDeviceLocation();
-
+ //TODO: consider implementing a button -> center map to current location
}
}
}
diff --git a/src/org/thoughtcrime/securesms/map/MapDataManager.java b/src/org/thoughtcrime/securesms/map/MapDataManager.java
new file mode 100644
index 000000000..3df6e414a
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/map/MapDataManager.java
@@ -0,0 +1,194 @@
+package org.thoughtcrime.securesms.map;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
+import android.graphics.drawable.Drawable;
+import android.support.annotation.DrawableRes;
+import android.support.annotation.NonNull;
+import android.support.v4.content.ContextCompat;
+import android.util.Log;
+
+import com.b44t.messenger.DcArray;
+import com.b44t.messenger.DcContact;
+import com.b44t.messenger.DcEventCenter;
+import com.mapbox.geojson.Feature;
+import com.mapbox.geojson.FeatureCollection;
+import com.mapbox.geojson.LineString;
+import com.mapbox.geojson.Point;
+import com.mapbox.mapboxsdk.maps.Style;
+import com.mapbox.mapboxsdk.style.layers.LineLayer;
+import com.mapbox.mapboxsdk.style.layers.Property;
+import com.mapbox.mapboxsdk.style.layers.PropertyFactory;
+import com.mapbox.mapboxsdk.style.layers.SymbolLayer;
+import com.mapbox.mapboxsdk.style.sources.GeoJsonSource;
+
+import org.thoughtcrime.securesms.ApplicationContext;
+import org.thoughtcrime.securesms.R;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import static com.b44t.messenger.DcContext.DC_EVENT_LOCATION_CHANGED;
+import static com.mapbox.mapboxsdk.style.layers.Property.ICON_ANCHOR_BOTTOM;
+
+/**
+ * Created by cyberta on 07.03.19.
+ */
+
+public class MapDataManager implements DcEventCenter.DcEventDelegate {
+ private static final String TAG = MapDataManager.class.getSimpleName();
+ private Style mapboxStyle;
+ private HashMap contactMapSources;
+ private int chatId;
+ private Context context;
+
+ public MapDataManager(Context context, @NonNull Style mapboxMapStyle, int chatId) {
+ this.mapboxStyle = mapboxMapStyle;
+ this.context = context;
+ this.chatId = chatId;
+ contactMapSources = new HashMap<>();
+ int[] contactIds = ApplicationContext.getInstance(context).dcContext.getChatContacts(chatId);
+ for (int contactId : contactIds) {
+ addContactMapSource(contactId);
+ updateSource(contactId);
+ }
+ }
+
+ public void onResume() {
+ ApplicationContext.getInstance(context).dcContext.eventCenter.addObserver(DC_EVENT_LOCATION_CHANGED, this);
+ updateSources();
+ }
+
+ public void onPause() {
+ ApplicationContext.getInstance(context).dcContext.eventCenter.removeObserver(DC_EVENT_LOCATION_CHANGED, this);
+ }
+
+ public void addContactMapSource(int contactId) {
+ DcContact contact = ApplicationContext.getInstance(context).dcContext.getContact(contactId);
+ MapSource contactMapSource = new MapSource(contactId);
+ contactMapSource.setColor(contact.getColor());
+ contactMapSources.put(contactId, contactMapSource);
+ initGeoJsonSources(contactMapSource);
+ initLayers(contactMapSource);
+ }
+
+ private Bitmap generateColoredLastPositionIcon(int colorFilter) {
+ return generateColoredBitmap(colorFilter, R.drawable.ic_location_on_white_24dp);
+ }
+
+ private Bitmap generateColoredLocationIcon(int colorFilter) {
+ return generateColoredBitmap(colorFilter, R.drawable.ic_location_dot);
+ }
+
+ private Bitmap generateColoredBitmap(int colorFilter, @DrawableRes int res) {
+ Bitmap icon = getBitmap(res);
+ Paint paint = new Paint();
+ ColorFilter filter = new PorterDuffColorFilter(colorFilter, PorterDuff.Mode.SRC_IN);
+ paint.setColorFilter(filter);
+ Canvas canvas = new Canvas(icon);
+ canvas.drawBitmap(icon, 0, 0, paint);
+ return icon;
+ }
+
+ private Bitmap getBitmap(@DrawableRes int res) {
+ Drawable drawable = ContextCompat.getDrawable(context, res);
+ Canvas canvas = new Canvas();
+ Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
+ canvas.setBitmap(bitmap);
+ drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
+ drawable.draw(canvas);
+ return bitmap;
+ }
+
+
+ private void updateSources() {
+ for (Integer contactId : contactMapSources.keySet()) {
+ updateSource(contactId);
+ }
+ }
+
+ private void updateSource(int contactId) {
+ DcArray locations = ApplicationContext.getInstance(context).dcContext.getLocations(chatId, contactId);
+ int count = locations.getCnt();
+ if (count == 0) {
+ return;
+ }
+
+ ArrayList coordinateList = new ArrayList<>();
+ ArrayList pointFeatureList = new ArrayList<>();
+
+ for (int i = 0; i < count; i++) {
+ Point p = Point.fromLngLat(locations.getLongitude(i), locations.getLatitude(i));
+ coordinateList.add(p);
+ pointFeatureList.add(Feature.fromGeometry(p));
+ }
+
+ FeatureCollection pointFeatureCollection = FeatureCollection.fromFeatures(pointFeatureList);
+ FeatureCollection lineFeatureCollection = FeatureCollection.fromFeatures(new Feature[] {Feature.fromGeometry(
+ LineString.fromLngLats(coordinateList)
+ )});
+
+ MapSource contactMapMetadata = contactMapSources.get(contactId);
+ GeoJsonSource lineSource = (GeoJsonSource) mapboxStyle.getSource(contactMapMetadata.getLineSource());
+ lineSource.setGeoJson(lineFeatureCollection);
+ GeoJsonSource pointSource = (GeoJsonSource) mapboxStyle.getSource(contactMapMetadata.getMarkerSource());
+ pointSource.setGeoJson(pointFeatureCollection);
+ GeoJsonSource lastPostionSource = (GeoJsonSource) mapboxStyle.getSource(contactMapMetadata.getLastPositionSource());
+ lastPostionSource.setGeoJson(pointFeatureList.get(0));
+ }
+
+ private void initGeoJsonSources(MapSource source) {
+ GeoJsonSource lastPositionSource = new GeoJsonSource(source.getLastPositionSource());
+ GeoJsonSource markerPositionSource = new GeoJsonSource(source.getMarkerSource());
+ GeoJsonSource linePositionSource = new GeoJsonSource(source.getLineSource());
+
+ try {
+ mapboxStyle.addSource(lastPositionSource);
+ mapboxStyle.addSource(markerPositionSource);
+ mapboxStyle.addSource(linePositionSource);
+ } catch (RuntimeException e) {
+ //TODO: specify exception more
+ Log.e(TAG, "Unable to init GeoJsonSources. Already added to mapBoxMap? " + e.getMessage());
+ }
+
+ }
+
+ private void initLayers(MapSource source) {
+ mapboxStyle.addImage(source.getMarkerLastPositon(),
+ generateColoredLastPositionIcon(source.getColorArgb()));
+ mapboxStyle.addImage(source.getMarkerIcon(),
+ generateColoredLocationIcon(source.getColorArgb()));
+
+
+ mapboxStyle.addLayer(new SymbolLayer(source.getMarkerLayer(), source.getMarkerSource())
+ .withProperties(PropertyFactory.iconImage(source.getMarkerIcon())));
+ mapboxStyle.addLayer(new SymbolLayer(source.getLastPositionLayer(), source.getLastPositionSource())
+ .withProperties(PropertyFactory.iconImage(source.getMarkerLastPositon()),
+ PropertyFactory.iconAnchor(ICON_ANCHOR_BOTTOM)));
+ mapboxStyle.addLayer(new LineLayer(source.getLineLayer(), source.getLineSource())
+ .withProperties(PropertyFactory.lineCap(Property.LINE_CAP_ROUND),
+ PropertyFactory.lineJoin(Property.LINE_JOIN_ROUND),
+ PropertyFactory.lineWidth(3f),
+ PropertyFactory.lineColor(source.getColorArgb())));
+ }
+
+
+ @Override
+ public void handleEvent(int eventId, Object data1, Object data2) {
+ int contactId = (Integer) data1;
+ if (contactMapSources.containsKey(contactId)) {
+ updateSource(contactId);
+ }
+ }
+
+ @Override
+ public boolean runOnMain() {
+ return false;
+ }
+
+}
diff --git a/src/org/thoughtcrime/securesms/map/MapSource.java b/src/org/thoughtcrime/securesms/map/MapSource.java
new file mode 100644
index 000000000..e4cb40219
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/map/MapSource.java
@@ -0,0 +1,92 @@
+package org.thoughtcrime.securesms.map;
+
+import android.graphics.Color;
+
+/**
+ * Created by cyberta on 07.03.19.
+ */
+
+public class MapSource {
+ public static final String LINE_LAYER = "line_layer";
+ public static final String MARKER_LAYER = "symbol_layer";
+ public static final String LAST_POSITON_LAYER = "last_position_layer";
+ public static final String LINE_SOURCE = "line_source";
+ public static final String MARKER_POSITION_SOURCE = "marker_position";
+ public static final String LAST_POSITION_SOURCE = "marker_last_position";
+ public static final String MARKER_ICON = "marker_icon_id";
+ public static final String MARKER_LAST_POSITON = "marker_last_position";
+
+ private final String markerSource;
+ private final String lineSource;
+ private final String lastPositionSource;
+
+ private final String markerLayer;
+ private final String lineLayer;
+ private final String lastPositionLayer;
+
+ private final String markerIcon;
+ private final String markerLastPositon;
+
+ private int color;
+ private int colorArgb;
+
+ public MapSource(int chatId) {
+ markerSource = MARKER_POSITION_SOURCE + "_" + chatId;
+ lineSource = LINE_SOURCE + "_" + chatId;
+ lastPositionSource = LAST_POSITION_SOURCE + "_" + chatId;
+ markerLayer = MARKER_LAYER + "_" + chatId;
+ lineLayer = LINE_LAYER + "_" + chatId;
+ lastPositionLayer = LAST_POSITON_LAYER + "_" + chatId;
+ markerIcon = MARKER_ICON + "_" + chatId;
+ markerLastPositon = MARKER_LAST_POSITON + "_" + chatId;
+ }
+
+ public void setColor(int color) {
+ this.color = color;
+ colorArgb = Color.argb(0xFF, Color.red(color), Color.green(color), Color.blue(color));
+ }
+
+ public int getColorArgb() {
+ return colorArgb;
+ }
+
+ public int getColor() {
+ return color;
+ }
+
+ public String getMarkerSource() {
+ return markerSource;
+ }
+
+ public String getLineSource() {
+ return lineSource;
+ }
+
+ public String getLastPositionSourceName() {
+ return lastPositionSource;
+ }
+
+ public String getMarkerLayer() {
+ return markerLayer;
+ }
+
+ public String getLineLayer() {
+ return lineLayer;
+ }
+
+ public String getLastPositionLayer() {
+ return lastPositionLayer;
+ }
+
+ public String getMarkerIcon() {
+ return markerIcon;
+ }
+
+ public String getMarkerLastPositon() {
+ return markerLastPositon;
+ }
+
+ public String getLastPositionSource() {
+ return lastPositionSource;
+ }
+}