diff --git a/OsmAnd-java/src/net/osmand/util/Algorithms.java b/OsmAnd-java/src/net/osmand/util/Algorithms.java index 7469863406..2c94bf125d 100644 --- a/OsmAnd-java/src/net/osmand/util/Algorithms.java +++ b/OsmAnd-java/src/net/osmand/util/Algorithms.java @@ -100,6 +100,31 @@ public class Algorithms { } } + + /** + * Parse the color string, and return the corresponding color-int. + * If the string cannot be parsed, throws an IllegalArgumentException + * exception. Supported formats are: + * #RRGGBB + * #AARRGGBB + * 'red', 'blue', 'green', 'black', 'white', 'gray', 'cyan', 'magenta', + * 'yellow', 'lightgray', 'darkgray' + */ + public static int parseColor(String colorString) { + if (colorString.charAt(0) == '#') { + // Use a long to avoid rollovers on #ffXXXXXX + long color = Long.parseLong(colorString.substring(1), 16); + if (colorString.length() == 7) { + // Set the alpha value + color |= 0x00000000ff000000; + } else if (colorString.length() != 9) { + throw new IllegalArgumentException("Unknown color " + colorString); //$NON-NLS-1$ + } + return (int)color; + } + throw new IllegalArgumentException("Unknown color " + colorString); //$NON-NLS-1$ + } + public static int extractFirstIntegerNumber(String s) { int i = 0; for (int k = 0; k < s.length(); k++) { diff --git a/OsmAnd/res/values-ru/strings.xml b/OsmAnd/res/values-ru/strings.xml index 5542c05a8d..f04c1be14f 100644 --- a/OsmAnd/res/values-ru/strings.xml +++ b/OsmAnd/res/values-ru/strings.xml @@ -1546,7 +1546,7 @@ Прерывание музыки во время объявления Прерывание музыки Поделиться маршрутом используя файл GPX - Уникальный ID устройства + Ключ доступа Настройка параметров мониторинга и установка персонального канала мониторинга OpenStreetMap-Monitoring - продвинутый живой мониторинг с множеством средств удалённого контроля http://osmo.mobi OsMo (Продвинутый онлайн-мониторинг) @@ -1661,5 +1661,5 @@ OsmAnd - открытый источник и активно развается. голубой синий маджента - + зелёный diff --git a/OsmAnd/res/values/strings.xml b/OsmAnd/res/values/strings.xml index e64efc4b02..153633ae05 100644 --- a/OsmAnd/res/values/strings.xml +++ b/OsmAnd/res/values/strings.xml @@ -9,6 +9,10 @@ 3. All your modified/created strings are in the top of the file (to make easier find what\'s translated). PLEASE: Have a look at http://code.google.com/p/osmand/wiki/UIConsistency, it may really improve your and our work :-) Thx - Hardy --> + User %1$s joined group %2$s + User %1$s left group %2$s + Show group notifications + Show toast messages when user joins or leavs the group Follow Sign in In order to create groups you need to be a registered user of OsMo. diff --git a/OsmAnd/src/net/osmand/plus/OsmandSettings.java b/OsmAnd/src/net/osmand/plus/OsmandSettings.java index afc859fa90..0089b68116 100644 --- a/OsmAnd/src/net/osmand/plus/OsmandSettings.java +++ b/OsmAnd/src/net/osmand/plus/OsmandSettings.java @@ -834,6 +834,8 @@ public class OsmandSettings { public final OsmandPreference OSMO_AUTO_SEND_LOCATIONS = new BooleanPreference("osmo_automatically_send_locations", false).makeGlobal(); + public final OsmandPreference OSMO_SHOW_GROUP_NOTIFICATIONS = new BooleanPreference("osmo_show_toast_notifications", true).makeGlobal(); + public final CommonPreference OSMO_SAVE_TRACK_INTERVAL = new IntPreference("osmo_save_track_interval", 5000).makeGlobal().cache(); public final OsmandPreference OSMO_GROUPS = new StringPreference("osmo_groups", "{}").makeGlobal(); diff --git a/OsmAnd/src/net/osmand/plus/download/DownloadFileHelper.java b/OsmAnd/src/net/osmand/plus/download/DownloadFileHelper.java index 0563a47902..94b89a9d50 100644 --- a/OsmAnd/src/net/osmand/plus/download/DownloadFileHelper.java +++ b/OsmAnd/src/net/osmand/plus/download/DownloadFileHelper.java @@ -44,7 +44,7 @@ public class DownloadFileHelper { return e != null && e.getMessage().equals("Interrupted"); } - private InputStream getInputStreamToDownload(final URL url, final boolean forceWifi) throws IOException { + public InputStream getInputStreamToDownload(final URL url, final boolean forceWifi) throws IOException { InputStream cis = new InputStream() { byte[] buffer = new byte[BUFFER_SIZE]; int bufLen = 0; diff --git a/OsmAnd/src/net/osmand/plus/osmo/OsMoControlDevice.java b/OsmAnd/src/net/osmand/plus/osmo/OsMoControlDevice.java index bae8a5a135..3554fcdff1 100644 --- a/OsmAnd/src/net/osmand/plus/osmo/OsMoControlDevice.java +++ b/OsmAnd/src/net/osmand/plus/osmo/OsMoControlDevice.java @@ -23,9 +23,12 @@ public class OsMoControlDevice implements OsMoReactor { private OsMoService service; private OsmandApplication app; private OsMoTracker tracker; + private OsMoPlugin plugin; - public OsMoControlDevice(OsmandApplication app, OsMoService service, OsMoTracker tracker) { + public OsMoControlDevice(OsmandApplication app, OsMoPlugin plugin, + OsMoService service, OsMoTracker tracker) { this.app = app; + this.plugin = plugin; this.service = service; this.tracker = tracker; service.registerReactor(this); @@ -96,6 +99,8 @@ public class OsMoControlDevice implements OsMoReactor { } } return true; + } else if(command.equals("TP")) { + plugin.getDownloadGpxTask(true).execute(obj); } return false; } diff --git a/OsmAnd/src/net/osmand/plus/osmo/OsMoGroups.java b/OsmAnd/src/net/osmand/plus/osmo/OsMoGroups.java index e725f4a111..3c4acb4613 100644 --- a/OsmAnd/src/net/osmand/plus/osmo/OsMoGroups.java +++ b/OsmAnd/src/net/osmand/plus/osmo/OsMoGroups.java @@ -7,7 +7,10 @@ import java.util.List; import java.util.Map; import net.osmand.Location; +import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandSettings; +import net.osmand.plus.GPXUtilities.GPXFile; +import net.osmand.plus.R; import net.osmand.plus.osmo.OsMoGroupsStorage.OsMoDevice; import net.osmand.plus.osmo.OsMoGroupsStorage.OsMoGroup; import net.osmand.plus.osmo.OsMoTracker.OsmoTrackerListener; @@ -16,6 +19,8 @@ import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; +import android.os.AsyncTask; + public class OsMoGroups implements OsMoReactor, OsmoTrackerListener { private static final String GROUP_NAME = "name"; @@ -29,11 +34,14 @@ public class OsMoGroups implements OsMoReactor, OsmoTrackerListener { private static final String DELETED = "deleted"; private static final String GROUP_TRACKER_ID = "group_tracker_id"; private static final String LAST_ONLINE = "last_online"; + private static final String TRACK = "track"; private OsMoTracker tracker; private OsMoService service; private OsMoGroupsStorage storage; private OsMoGroupsUIListener uiListener; + private OsMoPlugin plugin; + private OsmandApplication app; public interface OsMoGroupsUIListener { @@ -42,12 +50,14 @@ public class OsMoGroups implements OsMoReactor, OsmoTrackerListener { public void deviceLocationChanged(OsMoDevice device); } - public OsMoGroups(OsMoService service, OsMoTracker tracker, OsmandSettings settings) { + public OsMoGroups(OsMoPlugin plugin, OsMoService service, OsMoTracker tracker, OsmandApplication app) { + this.plugin = plugin; this.service = service; this.tracker = tracker; + this.app = app; service.registerReactor(this); tracker.setTrackerListener(this); - storage = new OsMoGroupsStorage(this, settings.OSMO_GROUPS); + storage = new OsMoGroupsStorage(this, app.getSettings().OSMO_GROUPS); storage.load(); } @@ -152,13 +162,23 @@ public class OsMoGroups implements OsMoReactor, OsmoTrackerListener { group = storage.getGroup(gid); if(group != null) { List delta = mergeGroup(group, obj, false); + StringBuilder b = new StringBuilder(); for(OsMoDevice d : delta) { if(d.getDeletedTimestamp() != 0 && d.isEnabled()) { + if(group.name != null) { + b.append(app.getString(R.string.osmo_user_left, d.getVisibleName(), group.getVisibleName(app))).append("\n"); + } disconnectImpl(d); } else if(!d.isActive()) { + if(group.name != null) { + b.append(app.getString(R.string.osmo_user_joined, d.getVisibleName(), group.getVisibleName(app))).append("\n"); + } connectDeviceImpl(d); } } + if(b.length() > 0 && app.getSettings().OSMO_SHOW_GROUP_NOTIFICATIONS.get()){ + app.showToastMessage(b.toString().trim()); + } storage.save(); } processed = true; @@ -277,11 +297,19 @@ public class OsMoGroups implements OsMoReactor, OsmoTrackerListener { device.lastOnline = o.getLong(LAST_ONLINE); } if (o.has(USER_COLOR)) { - device.serverColor = o.getInt(USER_COLOR); + device.serverColor = net.osmand.util.Algorithms.parseColor(o.getString(USER_COLOR)); } delta.add(device); } } + if(obj.has(TRACK)){ + JSONArray ar = obj.getJSONArray(TRACK); + JSONObject[] a = new JSONObject[ar.length()]; + for(int i = 0; i < a.length; i++) { + a[i] = (JSONObject) ar.get(i); + } + plugin.getDownloadGpxTask(true).execute(a); + } if(deleteUsers) { for(OsMoDevice s : toDelete.values()) { s.deleted = System.currentTimeMillis(); @@ -318,13 +346,17 @@ public class OsMoGroups implements OsMoReactor, OsmoTrackerListener { } storage.save(); } + public void setGenColor(OsMoDevice device, int genColor) { + device.genColor = genColor; + storage.save(); + } - public OsMoDevice addConnectedDevice(String trackerId, String nameUser, int userColor) { + public OsMoDevice addConnectedDevice(String trackerId, String nameUser, int genColor) { OsMoDevice us = new OsMoDevice(); us.group = storage.getMainGroup(); us.trackerId = trackerId; us.userName = nameUser; - us.userColor = userColor; + us.genColor = genColor; us.group.users.put(trackerId, us); connectDeviceImpl(us); storage.save(); @@ -340,7 +372,7 @@ public class OsMoGroups implements OsMoReactor, OsmoTrackerListener { } g.userName = userName; service.pushCommand(op); - return op; + return "GROUP_JOIN:"+groupId; } public String leaveGroup(OsMoGroup group) { diff --git a/OsmAnd/src/net/osmand/plus/osmo/OsMoGroupsActivity.java b/OsmAnd/src/net/osmand/plus/osmo/OsMoGroupsActivity.java index cc8a07e99c..7db76fb355 100644 --- a/OsmAnd/src/net/osmand/plus/osmo/OsMoGroupsActivity.java +++ b/OsmAnd/src/net/osmand/plus/osmo/OsMoGroupsActivity.java @@ -363,7 +363,7 @@ public class OsMoGroupsActivity extends OsmandExpandableListActivity implements createMenuItem(menu, GROUP_INFO, R.string.osmo_group_info, R.drawable.ic_action_info_light, R.drawable.ic_action_info_dark, MenuItem.SHOW_AS_ACTION_IF_ROOM); } - if (device == null || device.getGroup().isMainGroup()) { + if ((group != null && !group.isMainGroup()) || (device != null && device.getGroup().isMainGroup())) { createMenuItem(menu, DELETE_ACTION_ID, R.string.default_buttons_delete, R.drawable.ic_action_delete_light, R.drawable.ic_action_delete_dark, MenuItem.SHOW_AS_ACTION_IF_ROOM); @@ -958,7 +958,7 @@ public class OsMoGroupsActivity extends OsmandExpandableListActivity implements int activeColor = model.getColor(); if (activeColor == 0) { activeColor = getRandomColor(); - osMoPlugin.getGroups().setDeviceProperties(model, model.getVisibleName(), activeColor); + osMoPlugin.getGroups().setGenColor(model, activeColor); } //Location location = tracker.getLastLocation(model.trackerId); if (location == null || mapLocation == null) { diff --git a/OsmAnd/src/net/osmand/plus/osmo/OsMoGroupsStorage.java b/OsmAnd/src/net/osmand/plus/osmo/OsMoGroupsStorage.java index a66f688023..a4362021bd 100644 --- a/OsmAnd/src/net/osmand/plus/osmo/OsMoGroupsStorage.java +++ b/OsmAnd/src/net/osmand/plus/osmo/OsMoGroupsStorage.java @@ -6,6 +6,7 @@ import java.util.Collections; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; import net.osmand.Location; import net.osmand.data.LatLon; @@ -25,7 +26,8 @@ public class OsMoGroupsStorage { private static final String GROUP_ID = "group_id"; private static final String TRACKER_ID = "trackerId"; private static final String USER_NAME = "userName"; - private static final String USER_COLOR = "userColor"; + private static final String USER_COLOR = "userSetColor"; + private static final String GEN_COLOR = "genColor"; private static final String SERVER_COLOR = "serverColor"; private static final String DESCRIPTION = "description"; private static final String POLICY = "policy"; @@ -146,6 +148,9 @@ public class OsMoGroupsStorage { if (u.userColor != 0) { obj.put(USER_COLOR, u.userColor); } + if (u.genColor != 0) { + obj.put(GEN_COLOR, u.genColor); + } if (u.serverColor != 0) { obj.put(SERVER_COLOR, u.serverColor); } @@ -183,6 +188,9 @@ public class OsMoGroupsStorage { if(o.has(USER_COLOR)) { user.userColor = o.getInt(USER_COLOR); } + if(o.has(GEN_COLOR)) { + user.genColor = o.getInt(GEN_COLOR); + } if(o.has(DELETED)) { user.deleted = o.getLong(DELETED); } @@ -274,9 +282,6 @@ public class OsMoGroupsStorage { OsMoDevice d = users.get(trackerId); if(d != null) { d.setLastLocation(location); - if(location != null) { - d.setLastOnline(location.getTime()); - } } return d; } @@ -310,12 +315,14 @@ public class OsMoGroupsStorage { protected String userName; protected int serverColor; protected int userColor; + protected int genColor; protected String trackerId; protected boolean enabled; protected boolean active; protected long deleted; protected OsMoGroup group ; + protected ConcurrentLinkedQueue previousLocations = new java.util.concurrent.ConcurrentLinkedQueue(); protected List messages = new ArrayList(); protected long lastOnline; protected Location lastLocation; @@ -325,8 +332,21 @@ public class OsMoGroupsStorage { return messages; } + public ConcurrentLinkedQueue getPreviousLocations(long threshold) { + while(!previousLocations.isEmpty() && previousLocations.peek().getTime() < threshold) { + previousLocations.poll(); + } + return previousLocations; + } + public void setLastLocation(Location lastLocation) { + if(this.lastLocation != null) { + previousLocations.add(this.lastLocation); + } this.lastLocation = lastLocation; + if (lastLocation != null) { + setLastOnline(lastLocation.getTime()); + } } public Location getLastLocation() { @@ -336,6 +356,7 @@ public class OsMoGroupsStorage { public void setLastOnline(long lastOnline) { this.lastOnline = lastOnline; } + public long getLastOnline() { return lastOnline; } @@ -369,7 +390,7 @@ public class OsMoGroupsStorage { if(serverColor != 0) { return serverColor ; } - return userColor; + return genColor; } public String getVisibleName(){ diff --git a/OsmAnd/src/net/osmand/plus/osmo/OsMoPlugin.java b/OsmAnd/src/net/osmand/plus/osmo/OsMoPlugin.java index 12c56a1e25..20ee58dacc 100644 --- a/OsmAnd/src/net/osmand/plus/osmo/OsMoPlugin.java +++ b/OsmAnd/src/net/osmand/plus/osmo/OsMoPlugin.java @@ -1,18 +1,27 @@ package net.osmand.plus.osmo; -import java.text.SimpleDateFormat; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import net.osmand.IndexConstants; import net.osmand.Location; +import net.osmand.PlatformUtil; import net.osmand.data.LatLon; import net.osmand.plus.ApplicationMode; import net.osmand.plus.ContextMenuAdapter; import net.osmand.plus.ContextMenuAdapter.OnContextMenuClick; +import net.osmand.plus.GPXUtilities; +import net.osmand.plus.GPXUtilities.GPXFile; import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandPlugin; import net.osmand.plus.R; import net.osmand.plus.TargetPointsHelper; import net.osmand.plus.activities.MapActivity; import net.osmand.plus.activities.SettingsActivity; +import net.osmand.plus.download.DownloadFileHelper; import net.osmand.plus.osmo.OsMoGroupsStorage.OsMoDevice; import net.osmand.plus.osmo.OsMoService.SessionInfo; import net.osmand.plus.views.MapInfoLayer; @@ -22,10 +31,16 @@ import net.osmand.plus.views.OsmandMapLayer.DrawSettings; import net.osmand.plus.views.OsmandMapTileView; import net.osmand.plus.views.mapwidgets.BaseMapWidget; import net.osmand.plus.views.mapwidgets.TextInfoWidget; + +import org.apache.commons.logging.Log; +import org.json.JSONException; +import org.json.JSONObject; + import android.content.DialogInterface; import android.content.Intent; import android.graphics.Paint; import android.graphics.drawable.Drawable; +import android.os.AsyncTask; import android.preference.Preference; import android.preference.Preference.OnPreferenceClickListener; import android.preference.PreferenceScreen; @@ -40,16 +55,16 @@ public class OsMoPlugin extends OsmandPlugin implements MonitoringInfoControlSer private OsMoGroups groups; private BaseMapWidget osmoControl; private OsMoPositionLayer olayer; - - // 2014-05-27 23:11:40 - public static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + protected MapActivity mapActivity; + + private final static Log log = PlatformUtil.getLog(OsMoPlugin.class); public OsMoPlugin(final OsmandApplication app) { - service = new OsMoService(app); + service = new OsMoService(app, this); tracker = new OsMoTracker(service, app.getSettings().OSMO_SAVE_TRACK_INTERVAL, app.getSettings().OSMO_AUTO_SEND_LOCATIONS); - new OsMoControlDevice(app, service, tracker); - groups = new OsMoGroups(service, tracker, app.getSettings()); + new OsMoControlDevice(app, this, service, tracker); + groups = new OsMoGroups(this, service, tracker, app); this.app = app; ApplicationMode.regWidget("osmo_control", (ApplicationMode[])null); } @@ -80,6 +95,8 @@ public class OsMoPlugin extends OsmandPlugin implements MonitoringInfoControlSer public String getName() { return app.getString(R.string.osmo_plugin_name); } + + @Override public void updateLayers(OsmandMapTileView mapView, MapActivity activity) { @@ -157,13 +174,15 @@ public class OsMoPlugin extends OsmandPlugin implements MonitoringInfoControlSer @Override public void mapActivityPause(MapActivity activity) { groups.setUiListener(null); + mapActivity = activity; } @Override public void mapActivityResume(MapActivity activity) { - if(olayer != null) { + if (olayer != null) { groups.setUiListener(olayer); } + mapActivity = null; } /** @@ -305,6 +324,71 @@ public class OsMoPlugin extends OsmandPlugin implements MonitoringInfoControlSer return service; } - + public AsyncTask getDownloadGpxTask(final boolean makeVisible) { + + return new AsyncTask (){ + + @Override + protected String doInBackground(JSONObject... params) { + final File fl = app.getAppPath(IndexConstants.GPX_INDEX_DIR+"/osmo"); + if(!fl.exists()) { + fl.mkdirs(); + } + String errors = ""; + for(JSONObject obj : params) { + try { + File f = new File(fl, obj.getString("name")+".gpx"); + long timestamp = obj.getLong("created") * 1000; + boolean visible = obj.has("visible"); + boolean changed = false; + if(!f.exists() || (f.lastModified() != timestamp) ) { + boolean sizeEqual = f.exists() && obj.has("size") && obj.getLong("size") == f.length(); + if(sizeEqual && !f.setLastModified(timestamp - 1)){ + // false alarm + } else { + changed = true; + String url = obj.getString("url"); + log.info("Download gpx " + url); + DownloadFileHelper df = new DownloadFileHelper(app); + InputStream is = df.getInputStreamToDownload(new URL(url), false); + FileOutputStream fout = new FileOutputStream(f); + byte[] buf = new byte[1024]; + int k; + while ((k = is.read(buf)) >= 0) { + fout.write(buf, 0, k); + } + fout.close(); + is.close(); + if(!f.setLastModified(timestamp)) { + log.error("Timestamp updates are not supported"); + } + } + } + if(visible && (changed || makeVisible)) { + GPXFile selectGPXFile = GPXUtilities.loadGPXFile(app, f); + app.setGpxFileToDisplay(selectGPXFile, app.getSettings().SHOW_CURRENT_GPX_TRACK.get()); + } + } catch (JSONException e) { + e.printStackTrace(); + errors += e.getMessage() +"\n"; + } catch (IOException e) { + e.printStackTrace(); + errors += e.getMessage() +"\n"; + } + } + return errors; + } + + @Override + protected void onPostExecute(String result) { + if(result.length() > 0) { + app.showToastMessage(app.getString(R.string.osmo_io_error)+ result); + } + } + + }; + + } + } diff --git a/OsmAnd/src/net/osmand/plus/osmo/OsMoPositionLayer.java b/OsmAnd/src/net/osmand/plus/osmo/OsMoPositionLayer.java index 3c4c649182..486c9a0e67 100644 --- a/OsmAnd/src/net/osmand/plus/osmo/OsMoPositionLayer.java +++ b/OsmAnd/src/net/osmand/plus/osmo/OsMoPositionLayer.java @@ -2,7 +2,9 @@ package net.osmand.plus.osmo; import java.util.ArrayList; import java.util.Collection; +import java.util.Iterator; import java.util.List; +import java.util.concurrent.ConcurrentLinkedQueue; import net.osmand.Location; import net.osmand.access.AccessibleToast; @@ -25,7 +27,10 @@ import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; +import android.graphics.Paint.Cap; +import android.graphics.Paint.Join; import android.graphics.Paint.Style; +import android.graphics.Path; import android.graphics.PointF; import android.os.Handler; import android.util.DisplayMetrics; @@ -36,16 +41,19 @@ import android.widget.Toast; * Class represents a layer for osmo positions * */ -public class OsMoPositionLayer extends OsmandMapLayer implements ContextMenuLayer.IContextMenuProvider, OsMoGroupsUIListener { - +public class OsMoPositionLayer extends OsmandMapLayer implements ContextMenuLayer.IContextMenuProvider, OsMoGroupsUIListener, + ContextMenuLayer.IContextMenuProviderSelection{ + private DisplayMetrics dm; private final MapActivity map; private OsmandMapTileView view; - private Paint pointAltUI; - private Paint point; + private Paint pointInnerCircle; + private Paint pointOuter; private OsMoPlugin plugin; private final static float startZoom = 7; private Handler uiHandler; + private Paint paintPath; + private Path pth; public OsMoPositionLayer(MapActivity map, OsMoPlugin plugin) { this.map = map; @@ -60,14 +68,24 @@ public class OsMoPositionLayer extends OsmandMapLayer implements ContextMenuLaye WindowManager wmgr = (WindowManager) view.getContext().getSystemService(Context.WINDOW_SERVICE); wmgr.getDefaultDisplay().getMetrics(dm); - pointAltUI = new Paint(); - pointAltUI.setColor(view.getApplication().getResources().getColor(R.color.poi_background)); - pointAltUI.setStyle(Style.FILL); + pointInnerCircle = new Paint(); + pointInnerCircle.setColor(view.getApplication().getResources().getColor(R.color.poi_background)); + pointInnerCircle.setStyle(Style.FILL); + pointInnerCircle.setAntiAlias(true); - point = new Paint(); - point.setColor(Color.DKGRAY); - point.setAntiAlias(true); - point.setStyle(Style.FILL_AND_STROKE); + paintPath = new Paint(); + paintPath.setStyle(Style.STROKE); + paintPath.setStrokeWidth(14); + paintPath.setAntiAlias(true); + paintPath.setStrokeCap(Cap.ROUND); + paintPath.setStrokeJoin(Join.ROUND); + + pth = new Path(); + + pointOuter = new Paint(); + pointOuter.setColor(Color.GRAY); + pointOuter.setAntiAlias(true); + pointOuter.setStyle(Style.FILL_AND_STROKE); } public Collection getTrackingDevices() { @@ -79,30 +97,48 @@ public class OsMoPositionLayer extends OsmandMapLayer implements ContextMenuLaye final float zoom = tb.getZoom() + tb.getZoomScale(); if(zoom < startZoom){ r = 0; - } else if(zoom <= 15){ + } else if(zoom <= 11){ r = 10; - } else if(zoom <= 16){ - r = 14; - } else if(zoom <= 17){ - r = 16; + } else if(zoom <= 14){ + r = 12; } else { - r = 18; + r = 14; } return (int) (r * tb.getDensity()); } @Override public void onDraw(Canvas canvas, RotatedTileBox tb, DrawSettings nightMode) { - final int r = getRadiusPoi(tb) * 3 / 4; + final int r = getRadiusPoi(tb); + long treshold = System.currentTimeMillis() - 15000; for (OsMoDevice t : getTrackingDevices()) { Location l = t.getLastLocation(); if (l != null) { + ConcurrentLinkedQueue plocations = t.getPreviousLocations(treshold); int x = (int) tb.getPixXFromLatLon(l.getLatitude(), l.getLongitude()); int y = (int) tb.getPixYFromLatLon(l.getLatitude(), l.getLongitude()); - - pointAltUI.setColor(t.getColor()); - canvas.drawCircle(x, y, r , point); - canvas.drawCircle(x, y, r - 2, pointAltUI); + if (plocations.size() > 0) { + pth.rewind(); + Iterator it = plocations.iterator(); + boolean f= true; + while (it.hasNext()) { + Location lo = it.next(); + int xt = (int) tb.getPixXFromLatLon(lo.getLatitude(), lo.getLongitude()); + int yt = (int) tb.getPixYFromLatLon(lo.getLatitude(), lo.getLongitude()); + if(f) { + f = false; + pth.moveTo(xt, yt); + } else{ + pth.lineTo(xt, yt); + } + } + pth.lineTo(x, y); + paintPath.setColor(t.getColor()); + canvas.drawPath(pth, paintPath); + } + pointInnerCircle.setColor(t.getColor()); + canvas.drawCircle(x, y, r + 2, pointOuter); + canvas.drawCircle(x, y, r - 2, pointInnerCircle); } } } @@ -152,9 +188,11 @@ public class OsMoPositionLayer extends OsmandMapLayer implements ContextMenuLaye if (o instanceof OsMoDevice) { String d = map.getString(R.string.osmo_user_name) + " " + ((OsMoDevice) o).getVisibleName(); final Location l = ((OsMoDevice) o).getLastLocation(); + float speed = 0; if(l != null && l.hasSpeed()) { - d += "\n"+ OsmAndFormatter.getFormattedSpeed(l.getSpeed(), map.getMyApplication()); + speed = l.getSpeed(); } + d += "\n"+ OsmAndFormatter.getFormattedSpeed(speed, map.getMyApplication()); return d; } return null; @@ -162,10 +200,10 @@ public class OsMoPositionLayer extends OsmandMapLayer implements ContextMenuLaye @Override public String getObjectName(Object o) { - if(o instanceof OsMoDevice) { - return map.getString(R.string.osmo_user_name) + " " + ((OsMoDevice) o).getVisibleName(); - } - return null; +// if(o instanceof OsMoDevice) { +// return map.getString(R.string.osmo_user_name) + " " + ((OsMoDevice) o).getVisibleName(); +// } + return getObjectDescription(o); } public void refresh() { @@ -232,7 +270,6 @@ public class OsMoPositionLayer extends OsmandMapLayer implements ContextMenuLaye @Override public void deviceLocationChanged(final OsMoDevice device) { - boolean sameId = Algorithms.objectEquals(followTrackerId, device.trackerId); boolean sameDestId = Algorithms.objectEquals(followDestinationId, device.trackerId); Location l = device.getLastLocation(); if(sameDestId && l != null) { @@ -249,19 +286,30 @@ public class OsMoPositionLayer extends OsmandMapLayer implements ContextMenuLaye targets.navigateToPoint(lt, true, -1); } } - if(sameId && !schedule && l != null) { - schedule = true; + + boolean sameId = Algorithms.objectEquals(followTrackerId, device.trackerId); + if(sameId && !schedule && l != null) { ContextMenuLayer cl = map.getMapLayers().getContextMenuLayer(); - final boolean sameObject = Algorithms.objectEquals(device, cl.getFirstSelectedObject()) && cl.isVisible(); + final boolean sameObject; + if(cl.getFirstSelectedObject() instanceof OsMoDevice && cl.isVisible()) { + sameObject = Algorithms.objectEquals(device.trackerId, ((OsMoDevice) cl.getFirstSelectedObject()).trackerId) ; + } else{ + sameObject = false; + } LatLon mapLoc = new LatLon(map.getMapView().getLatitude(), map.getMapView().getLongitude()); final boolean centered = Algorithms.objectEquals(followMapLocation, mapLoc); if(sameObject || centered) { + final LatLon loc; if(centered ) { - followMapLocation = new LatLon(l.getLatitude(), l.getLongitude()); + loc = new LatLon(l.getLatitude(), l.getLongitude()); } else if(!map.getMapView().getAnimatedDraggingThread().isAnimating()) { // disable tracking - followMapLocation = null; + loc = null; + } else { + loc = followMapLocation; } + followMapLocation = loc; + schedule = true; uiHandler.postDelayed(new Runnable() { @Override @@ -274,8 +322,8 @@ public class OsMoPositionLayer extends OsmandMapLayer implements ContextMenuLaye cl.setSelectedObject(device); } if (centered) { - map.getMapView().setLatLon(followMapLocation.getLatitude(), - followMapLocation.getLongitude()); + map.getMapView().setLatLon(loc.getLatitude(), + loc.getLongitude()); } map.getMapView().refreshMap(); } @@ -286,6 +334,22 @@ public class OsMoPositionLayer extends OsmandMapLayer implements ContextMenuLaye } } } + + @Override + public void setSelectedObject(Object o) { + if(o instanceof OsMoDevice) { + followTrackerId = ((OsMoDevice) o).getTrackerId(); + } + } + + @Override + public void clearSelectedObjects() { + LatLon mapLoc = new LatLon(map.getMapView().getLatitude(), map.getMapView().getLongitude()); + final boolean centered = Algorithms.objectEquals(followMapLocation, mapLoc); + if(!centered && followTrackerId != null) { + followTrackerId = null; + } + } } diff --git a/OsmAnd/src/net/osmand/plus/osmo/OsMoService.java b/OsmAnd/src/net/osmand/plus/osmo/OsMoService.java index 72e656686d..db86275b5b 100644 --- a/OsmAnd/src/net/osmand/plus/osmo/OsMoService.java +++ b/OsmAnd/src/net/osmand/plus/osmo/OsMoService.java @@ -10,6 +10,7 @@ import java.util.List; import java.util.concurrent.ConcurrentLinkedQueue; import net.osmand.PlatformUtil; +import net.osmand.plus.GPXUtilities.GPXFile; import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; import net.osmand.plus.Version; @@ -27,6 +28,7 @@ import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; +import android.os.AsyncTask; import android.os.Build; import android.provider.Settings.Secure; @@ -41,12 +43,14 @@ public class OsMoService implements OsMoReactor { public static final String SHARE_TRACKER_URL = "http://z.osmo.mobi/connect?id="; public static final String SHARE_GROUP_URL = "http://z.osmo.mobi/join?id="; public static final String TRACK_URL = "http://test1342.osmo.mobi/u/"; - private String lastRegistrationError = null; + private String lastRegistrationError = null; + private OsMoPlugin plugin; - public OsMoService(OsmandApplication app) { + public OsMoService(OsmandApplication app, OsMoPlugin plugin) { this.app = app; + this.plugin = plugin; listReactors.add(this); } @@ -70,7 +74,7 @@ public class OsMoService implements OsMoReactor { if(thread == null) { return Collections.emptyList(); } - return new ArrayList(thread.getLast100Commands()); + return new ArrayList(thread.getLastCommands()); } @@ -242,6 +246,7 @@ public class OsMoService implements OsMoReactor { app.showToastMessage(app.getString(R.string.osmo_io_error) + string); } + public void pushCommand(String cmd) { commands.add(cmd); } @@ -269,13 +274,27 @@ public class OsMoService implements OsMoReactor { si.motd = data; } return true; + } else if(command.equals("TRACK_GET")) { + try { + JSONArray ar = new JSONArray(data); + AsyncTask task = plugin.getDownloadGpxTask(false); + JSONObject[] a = new JSONObject[ar.length()]; + for(int i = 0; i < a.length; i++) { + a[i] = (JSONObject) ar.get(i); + } + task.execute(a); + } catch (JSONException e) { + e.printStackTrace(); + showErrorMessage(e.getMessage()); + } } return false; } + @Override public void reconnect() { - + pushCommand("TRACK_GET"); } public void reconnectToServer() { diff --git a/OsmAnd/src/net/osmand/plus/osmo/OsMoThread.java b/OsmAnd/src/net/osmand/plus/osmo/OsMoThread.java index 7fd55ccc1d..e10da8cfc2 100644 --- a/OsmAnd/src/net/osmand/plus/osmo/OsMoThread.java +++ b/OsmAnd/src/net/osmand/plus/osmo/OsMoThread.java @@ -66,7 +66,8 @@ public class OsMoThread { private SimpleDateFormat df = new SimpleDateFormat("HH:mm:ss"); - private ConcurrentLinkedQueue last100Commands = new ConcurrentLinkedQueue(); + private ConcurrentLinkedQueue lastCommands = new ConcurrentLinkedQueue(); + private final static int STACK_CMD = 30; @@ -91,6 +92,7 @@ public class OsMoThread { this.activeChannel = null; authorized = 0; reconnect = false; + pingSent = 0; failures = 0; lastSendCommand = 0; selector = Selector.open(); @@ -276,7 +278,11 @@ public class OsMoThread { if(obj != null && obj.has("error")) { error = true; try { - service.showErrorMessage(obj.getString("error")); + String s = obj.getString("error"); + if(obj.has("error_description")) { + s += " " +obj.getString("error_description"); + } + service.showErrorMessage(s); } catch (JSONException e) { e.printStackTrace(); } @@ -378,23 +384,30 @@ public class OsMoThread { return ByteBuffer.wrap(prepareCommand(l).toString().getBytes("UTF-8")); } } - if(System.currentTimeMillis() - lastSendCommand > TIMEOUT_TO_PING) { - if(pingSent == 0) { + final long interval = System.currentTimeMillis() - lastSendCommand; + if(interval > TIMEOUT_TO_PING) { + final long pingInterval = System.currentTimeMillis() - pingSent; + if(pingSent == 0 || pingInterval > TIMEOUT_TO_PING) { pingSent = System.currentTimeMillis(); cmd(PING_CMD, true); return ByteBuffer.wrap(prepareCommand(PING_CMD).toString().getBytes("UTF-8")); } + } else if(pingSent != 0) { + pingSent = 0; } return null; } - public ConcurrentLinkedQueue getLast100Commands() { - return last100Commands; + public ConcurrentLinkedQueue getLastCommands() { + return lastCommands; } private void cmd(String cmd, boolean send) { log.info("OsMO" + (send ? "> " : ">> ") + cmd); - last100Commands.add((send ? "> " : ">> ") + df.format(new Date()) + " " + cmd); + lastCommands.add((send ? "> " : ">> ") + df.format(new Date()) + " " + cmd); + while(lastCommands.size() > STACK_CMD) { + lastCommands.poll(); + } } public SessionInfo getSessionInfo() { diff --git a/OsmAnd/src/net/osmand/plus/osmo/SettingsOsMoActivity.java b/OsmAnd/src/net/osmand/plus/osmo/SettingsOsMoActivity.java index f74fc35198..bdd000db44 100644 --- a/OsmAnd/src/net/osmand/plus/osmo/SettingsOsMoActivity.java +++ b/OsmAnd/src/net/osmand/plus/osmo/SettingsOsMoActivity.java @@ -28,7 +28,6 @@ public class SettingsOsMoActivity extends SettingsBaseActivity { private Preference debugPref; private Preference trackerId; - private CheckBoxPreference sendLocationsref; public static final int[] SECONDS = new int[] {0, 1, 2, 3, 5, 10, 15, 30, 60, 90}; public static final int[] MINUTES = new int[] {2, 3, 5}; @@ -48,7 +47,7 @@ public class SettingsOsMoActivity extends SettingsBaseActivity { trackerId.setOnPreferenceClickListener(this); grp.addPreference(trackerId); - sendLocationsref = createCheckBoxPreference(settings.OSMO_AUTO_SEND_LOCATIONS); + CheckBoxPreference sendLocationsref = createCheckBoxPreference(settings.OSMO_AUTO_SEND_LOCATIONS); sendLocationsref.setTitle(R.string.osmo_auto_send_locations); sendLocationsref.setSummary(R.string.osmo_auto_send_locations_descr); grp.addPreference(sendLocationsref); @@ -56,6 +55,11 @@ public class SettingsOsMoActivity extends SettingsBaseActivity { grp.addPreference(createTimeListPreference(settings.OSMO_SAVE_TRACK_INTERVAL, SECONDS, MINUTES, 1000, R.string.osmo_track_interval, R.string.osmo_track_interval_descr)); + CheckBoxPreference showGroupNotifiations = createCheckBoxPreference(settings.OSMO_SHOW_GROUP_NOTIFICATIONS); + sendLocationsref.setTitle(R.string.osmo_show_group_notifications); + sendLocationsref.setSummary(R.string.osmo_show_group_notifications_descr); + grp.addPreference(sendLocationsref); + debugPref = new Preference(this); debugPref.setTitle(R.string.osmo_settings_debug); debugPref.setOnPreferenceClickListener(this); diff --git a/OsmAnd/src/net/osmand/plus/views/ContextMenuLayer.java b/OsmAnd/src/net/osmand/plus/views/ContextMenuLayer.java index 73dd4ecba6..1ff0324c5b 100644 --- a/OsmAnd/src/net/osmand/plus/views/ContextMenuLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/ContextMenuLayer.java @@ -40,7 +40,17 @@ public class ContextMenuLayer extends OsmandMapLayer { public String getObjectName(Object o); + } + + public interface IContextMenuProviderSelection { + + public void setSelectedObject(Object o); + + public void clearSelectedObjects(); + + } + private static final String KEY_LAT_LAN = "context_menu_lat_lon"; private static final String KEY_DESCRIPTION = "context_menu_description"; private static final String KEY_SELECTED_OBJECTS = "context_menu_selected_objects"; @@ -189,17 +199,29 @@ public class ContextMenuLayer extends OsmandMapLayer { public void setSelections(Map selections) { + clearSelectedObjects(); + if (selections != null) { selectedObjects = selections; - } else { - selectedObjects.clear(); } if (!selectedObjects.isEmpty()) { Entry e = selectedObjects.entrySet().iterator().next(); latLon = e.getValue().getObjectLocation(e.getKey()); + if(e.getValue() instanceof IContextMenuProviderSelection){ + ((IContextMenuProviderSelection) e.getValue()).setSelectedObject(e.getKey()); + } } } + private void clearSelectedObjects() { + for(IContextMenuProvider p : this.selectedObjects.values()) { + if(p instanceof IContextMenuProviderSelection){ + ((IContextMenuProviderSelection) p).clearSelectedObjects(); + } + } + selectedObjects.clear(); + } + @Override public boolean onLongPressEvent(PointF point, RotatedTileBox tileBox) { if ((Build.VERSION.SDK_INT < 14) && !view.getSettings().SCROLL_MAP_BY_GESTURES.get()) { @@ -223,15 +245,19 @@ public class ContextMenuLayer extends OsmandMapLayer { public LatLon selectObjectsForContextMenu(RotatedTileBox tileBox, PointF point) { final double lat = tileBox.getLatFromPixel((int) point.x, (int) point.y); final double lon = tileBox.getLonFromPixel((int) point.x, (int) point.y); - selectedObjects.clear(); + clearSelectedObjects(); List s = new ArrayList(); LatLon latLon = null; - for(OsmandMapLayer l : view.getLayers()){ - if(l instanceof ContextMenuLayer.IContextMenuProvider){ + for(OsmandMapLayer lt : view.getLayers()){ + if(lt instanceof ContextMenuLayer.IContextMenuProvider){ s.clear(); - ((ContextMenuLayer.IContextMenuProvider) l).collectObjectsFromPoint(point, tileBox, s); + final IContextMenuProvider l = (ContextMenuLayer.IContextMenuProvider) lt; + l.collectObjectsFromPoint(point, tileBox, s); for(Object o : s) { - selectedObjects.put(o, ((ContextMenuLayer.IContextMenuProvider) l)); + selectedObjects.put(o, l); + if(l instanceof IContextMenuProviderSelection){ + ((IContextMenuProviderSelection) l).setSelectedObject(o); + } if(latLon == null) { latLon = ((ContextMenuLayer.IContextMenuProvider) l).getObjectLocation(o); } @@ -382,13 +408,16 @@ public class ContextMenuLayer extends OsmandMapLayer { } public void setSelectedObject(Object toShow) { - selectedObjects.clear(); + clearSelectedObjects(); if(toShow != null){ for(OsmandMapLayer l : view.getLayers()){ if(l instanceof ContextMenuLayer.IContextMenuProvider){ String des = ((ContextMenuLayer.IContextMenuProvider) l).getObjectDescription(toShow); if(des != null) { selectedObjects.put(toShow, (IContextMenuProvider) l); + if(l instanceof IContextMenuProviderSelection){ + ((IContextMenuProviderSelection) l).setSelectedObject(toShow); + } } } }