Add osmo service
This commit is contained in:
parent
ad6924f17a
commit
add5867aca
4 changed files with 384 additions and 143 deletions
|
@ -1,7 +1,5 @@
|
|||
package net.osmand.plus.osmo;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import net.osmand.Location;
|
||||
import net.osmand.PlatformUtil;
|
||||
import net.osmand.plus.ContextMenuAdapter;
|
||||
|
@ -9,18 +7,17 @@ import net.osmand.plus.ContextMenuAdapter.OnContextMenuClick;
|
|||
import net.osmand.plus.OsmandApplication;
|
||||
import net.osmand.plus.OsmandPlugin;
|
||||
import net.osmand.plus.R;
|
||||
import net.osmand.plus.activities.MapActivity;
|
||||
import net.osmand.plus.activities.SettingsActivity;
|
||||
import net.osmand.plus.views.MonitoringInfoControl;
|
||||
import net.osmand.plus.views.MonitoringInfoControl.MonitoringInfoControlServices;
|
||||
import net.osmand.plus.views.OsmandMapTileView;
|
||||
import net.osmand.plus.activities.MapActivity;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.os.AsyncTask;
|
||||
import android.preference.Preference;
|
||||
import android.preference.Preference.OnPreferenceClickListener;
|
||||
import android.preference.PreferenceScreen;
|
||||
|
@ -30,11 +27,13 @@ public class OsMoPlugin extends OsmandPlugin implements MonitoringInfoControlSer
|
|||
|
||||
private OsmandApplication app;
|
||||
public static final String ID = "osmand.osmo";
|
||||
private static final Log log = PlatformUtil.getLog(OsMoPlugin.class);
|
||||
//private static final Log log = PlatformUtil.getLog(OsMoPlugin.class);
|
||||
private OsMoService service;
|
||||
private OsMoTracker tracker;
|
||||
|
||||
public OsMoPlugin(final OsmandApplication app) {
|
||||
service = new OsMoService();
|
||||
tracker = new OsMoTracker(service);
|
||||
this.app = app;
|
||||
}
|
||||
|
||||
|
@ -45,13 +44,8 @@ public class OsMoPlugin extends OsmandPlugin implements MonitoringInfoControlSer
|
|||
|
||||
@Override
|
||||
public void updateLocation(Location location) {
|
||||
if (service.isActive()) {
|
||||
try {
|
||||
service.sendCoordinate(location.getLatitude(), location.getLongitude(), location.getAccuracy(),
|
||||
(float) location.getAltitude(), location.getSpeed(), location.getBearing());
|
||||
} catch (IOException e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
if (service.isConnected()) {
|
||||
tracker.sendCoordinate(location);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -79,73 +73,22 @@ public class OsMoPlugin extends OsmandPlugin implements MonitoringInfoControlSer
|
|||
}
|
||||
}
|
||||
|
||||
private void connect(final boolean enable) {
|
||||
new AsyncTask<Void, Void, String>() {
|
||||
private Exception e;
|
||||
@Override
|
||||
protected String doInBackground(Void... params) {
|
||||
try {
|
||||
String response;
|
||||
if (enable) {
|
||||
response = service.activate(getUUID(app));
|
||||
} else {
|
||||
response = service.deactivate();
|
||||
}
|
||||
return response;
|
||||
} catch (Exception e) {
|
||||
this.e = e;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
protected void onPostExecute(String result) {
|
||||
if(e != null) {
|
||||
app.showToastMessage(app.getString(R.string.osmo_io_error) + ": " + e.getMessage());
|
||||
log.error(e.getMessage(), e);
|
||||
} else {
|
||||
app.showToastMessage(result + "");
|
||||
}
|
||||
};
|
||||
|
||||
}.execute();
|
||||
}
|
||||
|
||||
private void sendCoordinate(final double lat, final double lon) {
|
||||
new AsyncTask<Void, Void, String>() {
|
||||
|
||||
private Exception e;
|
||||
|
||||
@Override
|
||||
protected String doInBackground(Void... params) {
|
||||
try {
|
||||
return service.sendCoordinate(lat, lon, 0, 0, 0,
|
||||
0);
|
||||
} catch (Exception e) {
|
||||
this.e = e;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
protected void onPostExecute(String result) {
|
||||
if(e != null) {
|
||||
app.showToastMessage(app.getString(R.string.osmo_io_error) + ": " + e.getMessage());
|
||||
log.error(e.getMessage(), e);
|
||||
} else {
|
||||
app.showToastMessage(result + "");
|
||||
}
|
||||
};
|
||||
|
||||
}.execute();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addMonitorActions(ContextMenuAdapter qa, MonitoringInfoControl li, final OsmandMapTileView view) {
|
||||
final boolean off = !service.isActive();
|
||||
final boolean off = !service.isConnected();
|
||||
qa.item(off ? R.string.osmo_mode_off : R.string.osmo_mode_on)
|
||||
.icon(off ? R.drawable.monitoring_rec_inactive : R.drawable.monitoring_rec_big)
|
||||
.listen(new OnContextMenuClick() {
|
||||
|
||||
@Override
|
||||
public void onContextMenuClick(int itemId, int pos, boolean isChecked, DialogInterface dialog) {
|
||||
connect(off);
|
||||
if(off) {
|
||||
service.connect(true);
|
||||
} else {
|
||||
service.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -158,7 +101,7 @@ public class OsMoPlugin extends OsmandPlugin implements MonitoringInfoControlSer
|
|||
public void onContextMenuClick(int itemId, int pos, boolean isChecked, DialogInterface dialog) {
|
||||
final double lat = view.getLatitude();
|
||||
final double lon = view.getLongitude();
|
||||
sendCoordinate(lat, lon);
|
||||
tracker.sendCoordinate(lat, lon);
|
||||
}
|
||||
}).reg();
|
||||
}
|
||||
|
|
|
@ -1,92 +1,68 @@
|
|||
package net.osmand.plus.osmo;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.net.Socket;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Queue;
|
||||
|
||||
import net.osmand.PlatformUtil;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import java.util.List;
|
||||
|
||||
public class OsMoService {
|
||||
private static String TRACKER_SERVER = "srv.osmo.mobi";
|
||||
private static int TRACKER_PORT = 4242;
|
||||
public static int NUMBER_OF_TRIES_TO_RECONNECT = 20;
|
||||
private Log log = PlatformUtil.getLog(OsMoService.class);
|
||||
private OutputStreamWriter out;
|
||||
private BufferedReader in;
|
||||
private Socket socket;
|
||||
private Queue<String> buffer = new LinkedList<String>();
|
||||
private int numberOfFailures = 0;
|
||||
private String loginHash;
|
||||
|
||||
public boolean isActive() {
|
||||
return socket != null;
|
||||
}
|
||||
|
||||
public String activate(String loginHash) throws IOException {
|
||||
this.loginHash = loginHash;
|
||||
socket = new Socket(TRACKER_SERVER, TRACKER_PORT);
|
||||
in = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
|
||||
out = new OutputStreamWriter(socket.getOutputStream(), "UTF-8");
|
||||
//public static int NUMBER_OF_TRIES_TO_RECONNECT = 20;
|
||||
|
||||
private OsMoThread thread;
|
||||
private List<OsMoSender> listSenders = new java.util.concurrent.CopyOnWriteArrayList<OsMoSender>();
|
||||
private List<OsMoReactor> listReactors = new java.util.concurrent.CopyOnWriteArrayList<OsMoReactor>();
|
||||
|
||||
public interface OsMoSender {
|
||||
|
||||
String t = sendCommand("auth|"+loginHash);
|
||||
t += sendCommand("session_open");
|
||||
return t;
|
||||
public String nextSendCommand();
|
||||
}
|
||||
|
||||
private String sendCommand(String s) throws IOException {
|
||||
s = prepareCommand(s);
|
||||
log.info("OSMo command : " + s);
|
||||
out.write(s);
|
||||
return in.readLine();
|
||||
}
|
||||
|
||||
private String prepareCommand(String s) {
|
||||
if(s.endsWith("\n")) {
|
||||
s += "\n";
|
||||
}
|
||||
return s;
|
||||
public interface OsMoReactor {
|
||||
|
||||
public boolean acceptCommand(String command);
|
||||
|
||||
}
|
||||
|
||||
public String sendCoordinate(double lat, double lon, float hdop, float alt, float speed, float bearing) throws IOException {
|
||||
return sendCommandWithBuffer("p|"+lat+":"+lon+":"+hdop+":"+alt+":"+speed+":"+bearing);
|
||||
public OsMoService() {
|
||||
}
|
||||
|
||||
private synchronized String sendCommandWithBuffer(String command) throws IOException {
|
||||
buffer.add(command);
|
||||
String lastResponse = null;
|
||||
while (!buffer.isEmpty()) {
|
||||
reconnectIfNeeded();
|
||||
try {
|
||||
lastResponse = sendCommand(command);
|
||||
} catch (IOException e) {
|
||||
numberOfFailures++;
|
||||
e.printStackTrace();
|
||||
|
||||
public boolean isConnected() {
|
||||
return thread != null && thread.isConnected();
|
||||
}
|
||||
|
||||
public boolean connect(boolean forceReconnect) {
|
||||
if(thread != null) {
|
||||
if(!forceReconnect ) {
|
||||
return isConnected();
|
||||
}
|
||||
thread.stopConnection();
|
||||
}
|
||||
return lastResponse;
|
||||
thread = new OsMoThread(listSenders, listReactors);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void reconnectIfNeeded() throws IOException {
|
||||
if(numberOfFailures >= NUMBER_OF_TRIES_TO_RECONNECT) {
|
||||
deactivate();
|
||||
activate(this.loginHash);
|
||||
numberOfFailures = 0;
|
||||
|
||||
public void disconnect() {
|
||||
if(thread != null) {
|
||||
thread.stopConnection();
|
||||
}
|
||||
}
|
||||
|
||||
public void registerSender(OsMoSender sender) {
|
||||
if(!listSenders.contains(sender)) {
|
||||
listSenders.add(sender);
|
||||
}
|
||||
}
|
||||
|
||||
public void registerReactor(OsMoReactor reactor) {
|
||||
if(!listReactors.contains(reactor)) {
|
||||
listReactors.add(reactor);
|
||||
}
|
||||
}
|
||||
|
||||
public String deactivate() throws IOException {
|
||||
String t = sendCommand("session_close");
|
||||
in.close();
|
||||
out.close();
|
||||
socket.close();
|
||||
socket = null;
|
||||
return t;
|
||||
public void removeSender(OsMoSender s) {
|
||||
listSenders.remove(s);
|
||||
}
|
||||
|
||||
public void removeReactor(OsMoReactor s) {
|
||||
listReactors.remove(s);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
273
OsmAnd/src/net/osmand/plus/osmo/OsMoThread.java
Normal file
273
OsmAnd/src/net/osmand/plus/osmo/OsMoThread.java
Normal file
|
@ -0,0 +1,273 @@
|
|||
package net.osmand.plus.osmo;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.SelectionKey;
|
||||
import java.nio.channels.Selector;
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import net.osmand.PlatformUtil;
|
||||
import net.osmand.plus.osmo.OsMoService.OsMoReactor;
|
||||
import net.osmand.plus.osmo.OsMoService.OsMoSender;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.Message;
|
||||
|
||||
public class OsMoThread {
|
||||
private static String TRACKER_SERVER = "srv.osmo.mobi";
|
||||
private static int TRACKER_PORT = 4242;
|
||||
|
||||
protected final static Log log = PlatformUtil.getLog(OsMoThread.class);
|
||||
private static final long HEARTBEAT_DELAY = 100;
|
||||
private static final long HEARTBEAT_FAILED_DELAY = 10000;
|
||||
private static final long LIMIT_OF_FAILURES_RECONNECT = 10;
|
||||
private static final long CONNECTION_DELAY = 25000;
|
||||
private static final long SELECT_TIMEOUT = 500;
|
||||
private static int HEARTBEAT_MSG = 3;
|
||||
private Handler serviceThread;
|
||||
|
||||
private int failures = 0;
|
||||
private int activeConnectionId = 0;
|
||||
// -1 means connected, 0 needs to reconnect, > 0 when connection initiated
|
||||
private long connectionStarted = 0;
|
||||
private boolean stopThread;
|
||||
private Selector selector;
|
||||
|
||||
private List<OsMoSender> listSenders;
|
||||
private List<OsMoReactor> listReactors;
|
||||
|
||||
private SocketChannel activeChannel;
|
||||
private ByteBuffer pendingSendCommand;
|
||||
private String readCommand = "";
|
||||
private ByteBuffer pendingReadCommand = ByteBuffer.allocate(2048);
|
||||
private LinkedList<String> queueOfMessages = new LinkedList<String>();
|
||||
|
||||
public OsMoThread(List<OsMoSender> listSenders, List<OsMoReactor> listReactors) {
|
||||
this.listSenders = listSenders;
|
||||
this.listReactors = listReactors;
|
||||
// start thread to receive events from OSMO
|
||||
HandlerThread h = new HandlerThread("OSMo Service");
|
||||
h.start();
|
||||
serviceThread = new Handler(h.getLooper());
|
||||
serviceThread.post(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
initConnection();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
scheduleHeartbeat(HEARTBEAT_DELAY);
|
||||
}
|
||||
|
||||
public void stopConnection() {
|
||||
stopThread = true;
|
||||
}
|
||||
|
||||
protected void initConnection() throws IOException {
|
||||
try {
|
||||
selector = Selector.open();
|
||||
connectionStarted = System.currentTimeMillis();
|
||||
activeChannel = SocketChannel.open();
|
||||
activeChannel.configureBlocking(false);
|
||||
SelectionKey key = activeChannel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
|
||||
key.attach(new Integer(++activeConnectionId));
|
||||
activeChannel.connect(new InetSocketAddress(TRACKER_SERVER, TRACKER_PORT));
|
||||
} catch (IOException e) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
public void scheduleHeartbeat(long delay) {
|
||||
Message msg = serviceThread.obtainMessage();
|
||||
msg.what = HEARTBEAT_MSG;
|
||||
serviceThread.postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
checkAsyncSocket();
|
||||
}
|
||||
}, delay);
|
||||
}
|
||||
|
||||
public boolean isConnected() {
|
||||
return connectionStarted == -1;
|
||||
}
|
||||
|
||||
protected void checkAsyncSocket() {
|
||||
long delay = HEARTBEAT_DELAY;
|
||||
try {
|
||||
if (selector == null) {
|
||||
stopThread = true;
|
||||
} else {
|
||||
if (activeChannel != null && connectionStarted != -1 && !activeChannel.isConnectionPending()) {
|
||||
// connection ready
|
||||
connectionStarted = -1;
|
||||
}
|
||||
if ((connectionStarted != -1 && System.currentTimeMillis() - connectionStarted > CONNECTION_DELAY)
|
||||
|| activeChannel == null) {
|
||||
initConnection();
|
||||
} else {
|
||||
checkSelectedKeys();
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.info("Exception selecting socket", e);
|
||||
e.printStackTrace();
|
||||
delay = HEARTBEAT_FAILED_DELAY;
|
||||
if (activeChannel != null && !activeChannel.isConnected()) {
|
||||
activeChannel = null;
|
||||
}
|
||||
if (failures++ > LIMIT_OF_FAILURES_RECONNECT) {
|
||||
stopChannel();
|
||||
}
|
||||
}
|
||||
if (stopThread) {
|
||||
stopChannel();
|
||||
serviceThread.getLooper().quit();
|
||||
} else {
|
||||
scheduleHeartbeat(delay);
|
||||
}
|
||||
}
|
||||
|
||||
private void stopChannel() {
|
||||
if (activeChannel != null) {
|
||||
try {
|
||||
activeChannel.close();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
activeChannel = null;
|
||||
}
|
||||
|
||||
private void checkSelectedKeys() throws IOException {
|
||||
/* int s = */selector.select(SELECT_TIMEOUT);
|
||||
Set<SelectionKey> keys = selector.selectedKeys();
|
||||
if (keys == null) {
|
||||
return;
|
||||
}
|
||||
Iterator<SelectionKey> iterator = keys.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
SelectionKey key = iterator.next();
|
||||
final boolean isActive = new Integer(activeConnectionId).equals(key.attachment());
|
||||
// final boolean isActive = activeChannel == key.channel();
|
||||
if (isActive) {
|
||||
if (key.isWritable()) {
|
||||
writeCommands();
|
||||
}
|
||||
if (key.isReadable()) {
|
||||
readCommands();
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
key.channel().close();
|
||||
} catch (Exception e) {
|
||||
log.info("Exception closing channel", e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
|
||||
private void readCommands() throws IOException {
|
||||
boolean hasSomethingToRead = true;
|
||||
while (hasSomethingToRead) {
|
||||
pendingReadCommand.clear();
|
||||
int read = activeChannel.read(pendingReadCommand);
|
||||
if (pendingReadCommand.hasRemaining()) {
|
||||
hasSomethingToRead = true;
|
||||
} else {
|
||||
hasSomethingToRead = false;
|
||||
}
|
||||
if (read > 0) {
|
||||
byte[] ar = pendingReadCommand.array();
|
||||
String res = new String(ar, 0, read);
|
||||
readCommand += res;
|
||||
int i;
|
||||
while ((i = readCommand.indexOf('\n')) != -1) {
|
||||
String cmd = readCommand.substring(0, i);
|
||||
readCommand = readCommand.substring(i + 1);
|
||||
queueOfMessages.add(cmd.replace("\\n", "\n"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (queueOfMessages.size() > 0) {
|
||||
while(!queueOfMessages.isEmpty()){
|
||||
String cmd = queueOfMessages.poll();
|
||||
boolean processed = false;
|
||||
for (OsMoReactor o : listReactors) {
|
||||
if (o.acceptCommand(cmd)) {
|
||||
processed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!processed) {
|
||||
log.warn("Command not processed '" + cmd + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void writeCommands() throws UnsupportedEncodingException, IOException {
|
||||
if (pendingSendCommand == null) {
|
||||
getNewPendingSendCommand();
|
||||
}
|
||||
while (pendingSendCommand != null) {
|
||||
activeChannel.write(pendingSendCommand);
|
||||
if (!pendingSendCommand.hasRemaining()) {
|
||||
pendingSendCommand = null;
|
||||
getNewPendingSendCommand();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void getNewPendingSendCommand() throws UnsupportedEncodingException {
|
||||
for (OsMoSender s : listSenders) {
|
||||
String l = s.nextSendCommand();
|
||||
if (l != null) {
|
||||
StringBuilder res = prepareCommand(l);
|
||||
pendingSendCommand = ByteBuffer.wrap(res.toString().getBytes("UTF-8"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private StringBuilder prepareCommand(String l) {
|
||||
boolean addNL = true;
|
||||
StringBuilder res = new StringBuilder();
|
||||
int i = 0;
|
||||
while (true) {
|
||||
int ni = l.indexOf('\n');
|
||||
if (ni == l.length() - 1) {
|
||||
res.append(l.substring(i));
|
||||
addNL = false;
|
||||
break;
|
||||
} else if (ni == -1) {
|
||||
res.append(l.substring(i));
|
||||
break;
|
||||
} else {
|
||||
res.append(l.substring(i, ni));
|
||||
res.append("\\").append("n");
|
||||
}
|
||||
i = ni + 1;
|
||||
}
|
||||
if (addNL) {
|
||||
l += "\n";
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
49
OsmAnd/src/net/osmand/plus/osmo/OsMoTracker.java
Normal file
49
OsmAnd/src/net/osmand/plus/osmo/OsMoTracker.java
Normal file
|
@ -0,0 +1,49 @@
|
|||
package net.osmand.plus.osmo;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import net.osmand.Location;
|
||||
import net.osmand.plus.osmo.OsMoService.OsMoSender;
|
||||
|
||||
public class OsMoTracker implements OsMoSender {
|
||||
private LinkedList<Location> bufferOfLocations = new LinkedList<Location>();
|
||||
|
||||
public OsMoTracker(OsMoService service) {
|
||||
service.registerSender(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String nextSendCommand() {
|
||||
if(!bufferOfLocations.isEmpty()){
|
||||
Location loc = bufferOfLocations.poll();
|
||||
StringBuilder cmd = new StringBuilder("T|");
|
||||
cmd.append("L").append((float)loc.getLatitude()).append(":").append((float)loc.getLongitude());
|
||||
if(loc.hasAccuracy()) {
|
||||
cmd.append("H").append((float)loc.getAccuracy());
|
||||
}
|
||||
if(loc.hasAltitude()) {
|
||||
cmd.append("A").append((float)loc.getAltitude());
|
||||
}
|
||||
if(loc.hasSpeed()) {
|
||||
cmd.append("S").append((float)loc.getSpeed());
|
||||
}
|
||||
return cmd.toString();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void sendCoordinate(Location location) {
|
||||
bufferOfLocations.add(location);
|
||||
}
|
||||
|
||||
public void sendCoordinate(double lat, double lon) {
|
||||
Location l = new Location("test");
|
||||
l.setLatitude(lat);
|
||||
l.setLongitude(lon);
|
||||
bufferOfLocations.add(l);
|
||||
}
|
||||
|
||||
|
||||
}
|
Loading…
Reference in a new issue