package net.osmand.plus.osmo; import net.osmand.Location; import net.osmand.PlatformUtil; import net.osmand.plus.OsmandSettings.OsmandPreference; import net.osmand.plus.notifications.OsmandNotification; import net.osmand.plus.notifications.OsmandNotification.NotificationType; import net.osmand.plus.osmo.OsMoGroupsStorage.OsMoDevice; import org.apache.commons.logging.Log; import org.json.JSONException; import org.json.JSONObject; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; import java.text.FieldPosition; import java.util.Collection; import java.util.Locale; import java.util.Map; import java.util.concurrent.ConcurrentLinkedQueue; public class OsMoTracker implements OsMoReactor { // 130.5143667 Maximum is 8 digits and minimum 1 private static final DecimalFormat floatingPointFormat = new DecimalFormat("0.0#######"); private static final DecimalFormat twoDigitsFormat = new DecimalFormat("0.0#"); private static final DecimalFormat integerFormat = new DecimalFormat("0"); static { DecimalFormatSymbols symbols = new DecimalFormatSymbols(Locale.ENGLISH); floatingPointFormat.setDecimalFormatSymbols(symbols); twoDigitsFormat.setDecimalFormatSymbols(symbols); integerFormat.setDecimalFormatSymbols(symbols); } private ConcurrentLinkedQueue<Location> bufferOfLocations = new ConcurrentLinkedQueue<>(); private OsMoService service; private int locationsSent = 0; private OsmoTrackerListener trackerListener = null; private Location lastSendLocation; private Location lastBufferLocation; private OsmandPreference<Integer> pref; private String sessionURL; private Map<String, OsMoDevice> trackingDevices = new java.util.concurrent.ConcurrentHashMap<>(); private OsmandPreference<Boolean> stateSendLocation; protected static final Log LOG = PlatformUtil.getLog(OsMoTracker.class); public interface OsmoTrackerListener { void locationChange(String trackerId, Location location); } public OsMoTracker(OsMoService service, OsmandPreference<Integer> interval, OsmandPreference<Boolean> stateSendLocation) { this.service = service; this.pref = interval; this.stateSendLocation = stateSendLocation; service.registerReactor(this); } public String getSessionURL() { if (!isEnabledTracker() || sessionURL == null) { return null; } return OsMoService.TRACK_URL + sessionURL; } public boolean isEnabledTracker() { return stateSendLocation.get(); } public void enableTracker() { if (!isEnabledTracker()) { enableTrackerCmd(); service.getMyApplication().getNotificationHelper().refreshNotification(NotificationType.OSMO); } } public void enableTrackerCmd() { stateSendLocation.set(true); service.pushCommand("TRACKER_SESSION_OPEN"); } public void disableTracker() { if (isEnabledTracker()) { stateSendLocation.set(false); service.pushCommand("TRACKER_SESSION_CLOSE"); } service.getMyApplication().getNotificationHelper().refreshNotification(NotificationType.OSMO); } public void startTrackingId(OsMoDevice d) { service.pushCommand("LISTEN:" + d.getTrackerId()); trackingDevices.put(d.getTrackerId(), d); } public void stopTrackingId(OsMoDevice d) { service.pushCommand("UNLISTEN:" + d.getTrackerId()); trackingDevices.remove(d.getTrackerId()); } @Override public String nextSendCommand(OsMoThread thread) { if (!bufferOfLocations.isEmpty()) { Location loc = bufferOfLocations.poll(); lastSendLocation = loc; locationsSent++; if ((System.currentTimeMillis() - loc.getTime()) > 2 * 60000 && loc.getTime() != 0) { return "B|" + formatLocation(loc); } else { return "T|" + formatLocation(loc); } } return null; } public static String formatLocation(Location loc) { StringBuffer cmd = new StringBuffer(); cmd.append("L"); floatingPointFormat.format(loc.getLatitude(), cmd, new FieldPosition(cmd.length())); cmd.append(":"); floatingPointFormat.format(loc.getLongitude(), cmd, new FieldPosition(cmd.length())); if (loc.hasAccuracy()) { cmd.append("H"); integerFormat.format(loc.getAccuracy(), cmd, new FieldPosition(cmd.length())); } if (loc.hasAltitude()) { cmd.append("A"); integerFormat.format(loc.getAltitude(), cmd, new FieldPosition(cmd.length())); } if (loc.hasSpeed() && (int) (loc.getSpeed() * 100) != 0) { cmd.append("S"); twoDigitsFormat.format(loc.getSpeed(), cmd, new FieldPosition(cmd.length())); } if (loc.hasBearing()) { cmd.append("C"); integerFormat.format(loc.getBearing(), cmd, new FieldPosition(cmd.length())); } if (loc.getTime() != 0) { cmd.append("T"); integerFormat.format(loc.getTime(), cmd, new FieldPosition(cmd.length())); } LOG.debug("formatLocation cmd=" + cmd); return cmd.toString(); } public Location getLastSendLocation() { return lastSendLocation; } public void sendCoordinate(Location location) { if (stateSendLocation.get() && location != null) { long ltime = lastBufferLocation == null ? 0 : lastBufferLocation.getTime(); if (location.getTime() - ltime > pref.get()) { if (lastBufferLocation != null && (!lastBufferLocation.hasSpeed() || lastBufferLocation.getSpeed() < 1) && lastBufferLocation.distanceTo(location) < 20) { if (lastBufferLocation != null && location.getTime() - ltime < 60000) { // ignores return; } } lastBufferLocation = location; bufferOfLocations.add(location); } } } public int getLocationsSent() { return locationsSent; } public int getBufferLocationsSize() { return bufferOfLocations.size(); } public void sendCoordinate(double lat, double lon) { Location l = new Location("test"); l.setTime(System.currentTimeMillis()); l.setLatitude(lat); l.setLongitude(lon); sendCoordinate(l); } @Override public boolean acceptCommand(String command, String tid, String data, JSONObject obj, OsMoThread thread) { switch (command) { case "LISTEN": return true; case "UNLISTEN": return true; case "TRACKER_SESSION_CLOSE": return true; case "TRACKER_SESSION_OPEN": try { sessionURL = obj.getString("url"); } catch (JSONException e) { service.showErrorMessage(e.getMessage()); e.printStackTrace(); } return true; case "LT": double lat = 0; double lon = 0; double speed = 0; int k = 0; for (int i = 1; i <= data.length(); i++) { boolean separator = i == data.length() || !(Character.isDigit(data.charAt(i)) || data.charAt(i) == ':' || data.charAt(i) == '.' || data.charAt(i) == '-'); if (separator) { char ch = data.charAt(k); String vl = data.substring(k + 1, i); if (ch == 'L') { int l = vl.indexOf(":"); lat = Double.parseDouble(vl.substring(0, l)); lon = Double.parseDouble(vl.substring(l + 1)); } else if (ch == 'S') { speed = Double.parseDouble(vl); } k = i; } } if (lat != 0 || lon != 0) { Location loc = new Location("osmo"); loc.setTime(System.currentTimeMillis()); loc.setLatitude(lat); loc.setLongitude(lon); if (speed > 0) { loc.setSpeed((float) speed); } if (trackerListener != null) { trackerListener.locationChange(tid, loc); } } return true; } return false; } public void setTrackerListener(OsmoTrackerListener trackerListener) { this.trackerListener = trackerListener; } public OsmoTrackerListener getTrackerListener() { return trackerListener; } public Collection<OsMoDevice> getTrackingDevices() { return trackingDevices.values(); } @Override public void onConnected() { if (stateSendLocation.get()) { enableTrackerCmd(); } } @Override public void onDisconnected(String msg) { } }