Skip to content

Commit 8e0c144

Browse files
committed
Fixed #29
Add Relay to Meshtastic, only for PLI/Chat, the atak.protos
1 parent 6e3a4e3 commit 8e0c144

4 files changed

Lines changed: 216 additions & 10 deletions

File tree

app/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
buildscript {
1010

1111

12-
ext.PLUGIN_VERSION = "1.0.27"
12+
ext.PLUGIN_VERSION = "1.0.28"
1313
ext.ATAK_VERSION = "4.10.0"
1414

1515
def takdevVersion = '2.+'

app/src/main/java/com/atakmap/android/meshtastic/MeshtasticMapComponent.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -270,8 +270,8 @@ public static byte[] getConfig() {
270270
} catch (RemoteException e) {
271271
e.printStackTrace();
272272
Log.d(TAG, "getConfig failed");
273-
return null;
274273
}
274+
return null;
275275
}
276276

277277
public static List<NodeInfo> getNodes() {
@@ -281,18 +281,19 @@ public static List<NodeInfo> getNodes() {
281281
} catch (Exception e) {
282282
e.printStackTrace();
283283
Log.d(TAG, "getNodes failed");
284-
return null;
285284
}
285+
return null;
286286
}
287+
287288
public static MyNodeInfo getMyNodeInfo() {
288289
try {
289290
if (mMeshService != null)
290291
return mMeshService.getMyNodeInfo();
291292
} catch (RemoteException e) {
292293
e.printStackTrace();
293294
Log.d(TAG, "getMyNodeInfo failed");
294-
return null;
295295
}
296+
return null;
296297
}
297298

298299
public static String getMyNodeID() {
@@ -302,8 +303,8 @@ public static String getMyNodeID() {
302303
} catch (RemoteException e) {
303304
e.printStackTrace();
304305
Log.d(TAG, "getMyNodeID failed");
305-
return "";
306306
}
307+
return "";
307308
}
308309
public static IMeshService getMeshService() {
309310
return mMeshService;

app/src/main/java/com/atakmap/android/meshtastic/MeshtasticReceiver.java

Lines changed: 204 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import android.content.Intent;
1515

1616
import android.content.SharedPreferences;
17+
import android.os.Bundle;
1718
import android.os.Environment;
1819
import android.os.RemoteException;
1920
import android.preference.PreferenceManager;
@@ -23,10 +24,15 @@
2324
import androidx.core.app.NotificationCompat;
2425

2526
import com.atakmap.android.chat.ChatDatabase;
27+
import com.atakmap.android.chat.ChatManagerMapComponent;
28+
import com.atakmap.android.chat.GeoChatConnectorHandler;
29+
import com.atakmap.android.contact.Contact;
30+
import com.atakmap.android.contact.Contacts;
2631
import com.atakmap.android.cot.CotMapComponent;
2732
import com.atakmap.android.maps.MapItem;
2833
import com.atakmap.android.maps.MapView;
2934
import com.atakmap.android.meshtastic.plugin.R;
35+
import com.atakmap.comms.CotServiceRemote;
3036
import com.atakmap.coremap.cot.event.CotDetail;
3137
import com.atakmap.coremap.cot.event.CotEvent;
3238
import com.atakmap.coremap.cot.event.CotPoint;
@@ -51,19 +57,25 @@
5157

5258
import java.io.File;
5359
import java.io.IOException;
60+
import java.io.StringReader;
5461
import java.nio.file.Files;
62+
import java.util.ArrayList;
5563
import java.util.HashMap;
64+
import java.util.List;
5665
import java.util.Locale;
5766
import java.util.UUID;
5867
import java.util.concurrent.atomic.AtomicReference;
5968

6069
import com.atakmap.android.meshtastic.plugin.*;
6170

62-
public class MeshtasticReceiver extends BroadcastReceiver {
71+
import org.xmlpull.v1.XmlPullParser;
72+
import org.xmlpull.v1.XmlPullParserException;
73+
import org.xmlpull.v1.XmlPullParserFactory;
74+
75+
public class MeshtasticReceiver extends BroadcastReceiver implements CotServiceRemote.CotEventListener {
6376
private final String TAG = "MeshtasticReceiver";
6477
private SharedPreferences prefs;
6578
private SharedPreferences.Editor editor;
66-
6779
private int oldModemPreset;
6880
private String sender;
6981
private static NotificationManager mNotifyManager;
@@ -447,7 +459,7 @@ public void onReceive(Context context, Intent intent) {
447459

448460
CotDetail takvDetail = new CotDetail("takv");
449461
takvDetail.setAttribute("platform", "Meshtastic Plugin");
450-
takvDetail.setAttribute("version", "1.0.25" + "\n----NodeInfo----\n" + ni.toString());
462+
takvDetail.setAttribute("version", "1.0.27" + "\n----NodeInfo----\n" + ni.toString());
451463
takvDetail.setAttribute("device", ni.getUser().getHwModelString());
452464
takvDetail.setAttribute("os", "1");
453465
cotDetail.addChild(takvDetail);
@@ -461,7 +473,6 @@ public void onReceive(Context context, Intent intent) {
461473
contactDetail.setAttribute("endpoint", "0.0.0.0:4242:tcp");
462474
cotDetail.addChild(contactDetail);
463475

464-
465476
if (cotEvent.isValid()) {
466477
CotMapComponent.getInternalDispatcher().dispatch(cotEvent);
467478
if (prefs.getBoolean("plugin_meshtastic_server", false)) {
@@ -880,7 +891,7 @@ else if (team.equals("DarkGreen"))
880891
if (cotEvent.isValid()) {
881892
CotMapComponent.getInternalDispatcher().dispatch(cotEvent);
882893
if (prefs.getBoolean("plugin_meshtastic_server", false)) {
883-
//CotMapComponent.getExternalDispatcher().dispatch(cotEvent);
894+
CotMapComponent.getExternalDispatcher().dispatch(cotEvent);
884895
}
885896
} else
886897
Log.e(TAG, "cotEvent was not valid");
@@ -980,4 +991,192 @@ else if (team.equals("DarkGreen"))
980991
}
981992
}
982993
}
994+
995+
@Override
996+
public void onCotEvent(CotEvent cotEvent, Bundle bundle) {
997+
if (prefs.getBoolean("plugin_meshtastic_from_server", false)) {
998+
if (cotEvent.isValid()) {
999+
1000+
int hopLimit = prefs.getInt("plugin_meshtastic_hop_limit", 3);
1001+
if (hopLimit > 8) {
1002+
hopLimit = 8;
1003+
}
1004+
1005+
int channel = prefs.getInt("plugin_meshtastic_channel", 0);
1006+
1007+
1008+
DataPacket dp = null;
1009+
int eventType = -1;
1010+
double divisor = 1e-7;
1011+
XmlPullParserFactory factory = null;
1012+
XmlPullParser xpp = null;
1013+
String callsign = null;
1014+
String deviceCallsign = null;
1015+
String message = null;
1016+
1017+
CotDetail cotDetail = cotEvent.getDetail();
1018+
1019+
try {
1020+
factory = XmlPullParserFactory.newInstance();
1021+
factory.setNamespaceAware(true);
1022+
xpp = factory.newPullParser();
1023+
xpp.setInput(new StringReader(cotDetail.toString()));
1024+
eventType = xpp.getEventType();
1025+
} catch (XmlPullParserException e) {
1026+
e.printStackTrace();
1027+
return;
1028+
}
1029+
1030+
// All Chat Rooms
1031+
if (cotEvent.getType().startsWith("b-t-f") && cotEvent.getUID().contains("All Chat Rooms")) {
1032+
1033+
try {
1034+
while (eventType != XmlPullParser.END_DOCUMENT) {
1035+
if (eventType == XmlPullParser.START_TAG) {
1036+
Log.d(TAG, xpp.getName());
1037+
if (xpp.getName().equalsIgnoreCase("remarks")) {
1038+
if (xpp.next() == XmlPullParser.TEXT)
1039+
message = xpp.getText();
1040+
} else if (xpp.getName().equalsIgnoreCase("__chat")) {
1041+
int attributeCount = xpp.getAttributeCount();
1042+
Log.d(TAG, "__chat has " + +attributeCount);
1043+
for (int i = 0; i < attributeCount; i++) {
1044+
if (xpp.getAttributeName(i).equalsIgnoreCase("senderCallsign"))
1045+
callsign = xpp.getAttributeValue(i);
1046+
}
1047+
} else if (xpp.getName().equalsIgnoreCase("link")) {
1048+
int attributeCount = xpp.getAttributeCount();
1049+
Log.d(TAG, "link has " + +attributeCount);
1050+
for (int i = 0; i < attributeCount; i++) {
1051+
if (xpp.getAttributeName(i).equalsIgnoreCase("uid"))
1052+
deviceCallsign = xpp.getAttributeValue(i);
1053+
}
1054+
}
1055+
}
1056+
eventType = xpp.next();
1057+
}
1058+
1059+
} catch (XmlPullParserException | IOException e) {
1060+
e.printStackTrace();
1061+
}
1062+
1063+
ATAKProtos.Contact.Builder contact = ATAKProtos.Contact.newBuilder();
1064+
contact.setCallsign(callsign);
1065+
contact.setDeviceCallsign(deviceCallsign);
1066+
1067+
ATAKProtos.GeoChat.Builder geochat = ATAKProtos.GeoChat.newBuilder();
1068+
geochat.setMessage(message);
1069+
geochat.setTo("All Chat Rooms");
1070+
1071+
ATAKProtos.TAKPacket.Builder tak_packet = ATAKProtos.TAKPacket.newBuilder();
1072+
tak_packet.setContact(contact);
1073+
tak_packet.setChat(geochat);
1074+
1075+
Log.d(TAG, "Total wire size for TAKPacket: " + tak_packet.build().toByteArray().length);
1076+
Log.d(TAG, "Sending: " + tak_packet.build().toString());
1077+
1078+
dp = new DataPacket(DataPacket.ID_BROADCAST, tak_packet.build().toByteArray(), Portnums.PortNum.ATAK_PLUGIN_VALUE, DataPacket.ID_LOCAL, System.currentTimeMillis(), 0, MessageStatus.UNKNOWN, hopLimit, channel);
1079+
try {
1080+
if (MeshtasticMapComponent.getMeshService() != null)
1081+
MeshtasticMapComponent.getMeshService().send(dp);
1082+
} catch (RemoteException e) {
1083+
throw new RuntimeException(e);
1084+
}
1085+
} else if (cotDetail.getAttribute("contact") != null) {
1086+
for (Contact c : Contacts.getInstance().getAllContacts()) {
1087+
if (cotEvent.getUID().equals(c.getUid())) {
1088+
1089+
int battery = 0, course = 0, speed = 0;
1090+
String role = null, name = null;
1091+
double lat = 0, lng = 0, alt = 0;
1092+
1093+
lat = cotEvent.getGeoPoint().getLatitude();
1094+
lng = cotEvent.getGeoPoint().getLongitude();
1095+
alt = cotEvent.getGeoPoint().getAltitude();
1096+
1097+
try {
1098+
while (eventType != XmlPullParser.END_DOCUMENT) {
1099+
if (eventType == XmlPullParser.START_TAG) {
1100+
if (xpp.getName().equalsIgnoreCase("contact")) {
1101+
int attributeCount = xpp.getAttributeCount();
1102+
Log.d(TAG, "Contact has " + attributeCount);
1103+
for (int i = 0; i < attributeCount; i++) {
1104+
if (xpp.getAttributeName(i).equalsIgnoreCase("callsign"))
1105+
callsign = xpp.getAttributeValue(i);
1106+
}
1107+
} else if (xpp.getName().equalsIgnoreCase("__group")) {
1108+
int attributeCount = xpp.getAttributeCount();
1109+
Log.d(TAG, "__group has " + attributeCount);
1110+
for (int i = 0; i < attributeCount; i++) {
1111+
if (xpp.getAttributeName(i).equalsIgnoreCase("role"))
1112+
role = xpp.getAttributeValue(i);
1113+
else if (xpp.getAttributeName(i).equalsIgnoreCase("name"))
1114+
name = xpp.getAttributeValue(i);
1115+
}
1116+
} else if (xpp.getName().equalsIgnoreCase("status")) {
1117+
int attributeCount = xpp.getAttributeCount();
1118+
Log.d(TAG, "status has " + attributeCount);
1119+
for (int i = 0; i < attributeCount; i++) {
1120+
if (xpp.getAttributeName(i).equalsIgnoreCase("battery"))
1121+
battery = Integer.parseInt(xpp.getAttributeValue(i));
1122+
}
1123+
} else if (xpp.getName().equalsIgnoreCase("track")) {
1124+
int attributeCount = xpp.getAttributeCount();
1125+
Log.d(TAG, "track has " + attributeCount);
1126+
for (int i = 0; i < attributeCount; i++) {
1127+
if (xpp.getAttributeName(i).equalsIgnoreCase("course"))
1128+
course = Double.valueOf(xpp.getAttributeValue(i)).intValue();
1129+
else if (xpp.getAttributeName(i).equalsIgnoreCase("speed"))
1130+
speed = Double.valueOf(xpp.getAttributeValue(i)).intValue();
1131+
}
1132+
}
1133+
}
1134+
eventType = xpp.next();
1135+
}
1136+
} catch (XmlPullParserException | IOException e) {
1137+
e.printStackTrace();
1138+
return;
1139+
}
1140+
1141+
ATAKProtos.Contact.Builder contact = ATAKProtos.Contact.newBuilder();
1142+
contact.setCallsign(callsign);
1143+
contact.setDeviceCallsign(cotEvent.getUID());
1144+
1145+
ATAKProtos.Group.Builder group = ATAKProtos.Group.newBuilder();
1146+
group.setRole(ATAKProtos.MemberRole.valueOf(role.replace(" ", "")));
1147+
group.setTeam(ATAKProtos.Team.valueOf(name.replace(" ", "")));
1148+
1149+
ATAKProtos.Status.Builder status = ATAKProtos.Status.newBuilder();
1150+
status.setBattery(battery);
1151+
1152+
ATAKProtos.PLI.Builder pli = ATAKProtos.PLI.newBuilder();
1153+
pli.setAltitude(Double.valueOf(alt).intValue());
1154+
pli.setLatitudeI((int) (lat / divisor));
1155+
pli.setLongitudeI((int) (lng / divisor));
1156+
pli.setCourse(course);
1157+
pli.setSpeed(speed);
1158+
1159+
ATAKProtos.TAKPacket.Builder tak_packet = ATAKProtos.TAKPacket.newBuilder();
1160+
tak_packet.setContact(contact);
1161+
tak_packet.setStatus(status);
1162+
tak_packet.setGroup(group);
1163+
tak_packet.setPli(pli);
1164+
1165+
Log.d(TAG, "Total wire size for TAKPacket: " + tak_packet.build().toByteArray().length);
1166+
Log.d(TAG, "Sending: " + tak_packet.build().toString());
1167+
1168+
dp = new DataPacket(DataPacket.ID_BROADCAST, tak_packet.build().toByteArray(), Portnums.PortNum.ATAK_PLUGIN_VALUE, DataPacket.ID_LOCAL, System.currentTimeMillis(), 0, MessageStatus.UNKNOWN, hopLimit, channel);
1169+
try {
1170+
if (MeshtasticMapComponent.getMeshService() != null)
1171+
MeshtasticMapComponent.getMeshService().send(dp);
1172+
} catch (RemoteException e) {
1173+
throw new RuntimeException(e);
1174+
}
1175+
}
1176+
}
1177+
}
1178+
}
1179+
}
1180+
}
1181+
9831182
}

app/src/main/res/xml/preferences.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@
77
android:summary="If enabled, messages from Meshtastic will relay to connected TAK servers"
88
android:dialogTitle="Enable relay to server"
99
android:defaultValue="false"/>
10+
<com.atakmap.android.gui.PanSwitchPreference
11+
android:key="plugin_meshtastic_from_server"
12+
android:title="Enable relay from server"
13+
android:summary="If enabled, messages from TAK Server will relay to Meshtastic (PLI/Chat only)"
14+
android:dialogTitle="Enable relay from server"
15+
android:defaultValue="false"/>
1016
<com.atakmap.android.gui.PanSwitchPreference
1117
android:key="plugin_meshtastic_tracker"
1218
android:title="Show all Meshtastic devices"

0 commit comments

Comments
 (0)