diff --git a/codec2talkie/build.gradle b/codec2talkie/build.gradle index 3a12ba0..cf39395 100644 --- a/codec2talkie/build.gradle +++ b/codec2talkie/build.gradle @@ -10,8 +10,8 @@ android { applicationId "com.radio.codec2talkie" minSdkVersion 23 targetSdkVersion 30 - versionCode 162 - versionName "1.62" + versionCode 163 + versionName "1.63" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } diff --git a/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/aprs/AprsDataTextMessage.java b/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/aprs/AprsDataTextMessage.java index 880863c..790434f 100644 --- a/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/aprs/AprsDataTextMessage.java +++ b/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/aprs/AprsDataTextMessage.java @@ -5,6 +5,7 @@ import com.radio.codec2talkie.protocol.position.Position; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; +import java.util.Locale; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -14,6 +15,7 @@ public class AprsDataTextMessage implements AprsData { public String dstCallsign; public String digipath; public String textMessage; + public Integer ackId; private boolean _isValid; @@ -37,6 +39,7 @@ public class AprsDataTextMessage implements AprsData { this.dstCallsign = textMessage.dst; this.textMessage = textMessage.text; this.digipath = textMessage.digipath; + this.ackId = textMessage.ackId; _isValid = true; } @@ -52,6 +55,7 @@ public class AprsDataTextMessage implements AprsData { textMessage.dst = this.dstCallsign; textMessage.digipath = this.digipath; textMessage.text = this.textMessage; + textMessage.ackId = this.ackId; return textMessage; } @@ -62,25 +66,56 @@ public class AprsDataTextMessage implements AprsData { this.digipath = digipath; this.srcCallsign = srcCallsign; ByteBuffer buffer = ByteBuffer.wrap(infoData); + // callsign, trim ending spaces byte[] callsign = new byte[9]; buffer.get(callsign); this.dstCallsign = new String(callsign).replaceAll("\\s+$", ""); + // ':' separator byte b = buffer.get(); if (b != ':') return; + // message byte[] message = new byte[buffer.remaining()]; buffer.get(message); - textMessage = new String(message, StandardCharsets.UTF_8); - // TODO, message id: {xxxxx (for auto ack) + String stringMessage = new String(message, StandardCharsets.UTF_8); + + // ack/rej message + this.ackId = 0; + Pattern p = Pattern.compile("^(ack|rej)(\n+){1,5}$", Pattern.DOTALL); + Matcher m = p.matcher(stringMessage); + if (m.find()) { + String type = m.group(1); + if (type != null) { + String ackIdStr = m.group(2); + if (ackIdStr != null) + this.ackId = Integer.parseInt(ackIdStr); + } + } else { + // message requires acknowledge {xxxxx (for auto ack) + p = Pattern.compile("^(.+){0,67}[{](\\d+){1,5}$", Pattern.DOTALL); + m = p.matcher(stringMessage); + if (m.find()) { + this.textMessage = m.group(1); + String ackNumStr = m.group(2); + if (ackNumStr != null) + this.ackId = Integer.parseInt(ackNumStr); + } else if (stringMessage.length() <= 67) { + this.textMessage = stringMessage; + } + } + // TODO, telemetry, make subclass from message, extend and extract values - _isValid = !isTelemetry(textMessage); + if (this.textMessage != null) + _isValid = !isTelemetry(this.textMessage); } @Override public byte[] toBinary() { - return String.format(":%-9s:%s", dstCallsign, textMessage).getBytes(); + return (ackId > 0) + ? String.format(Locale.US, ":%-9s:%s{%d", dstCallsign, textMessage, ackId).getBytes() + : String.format(":%-9s:%s", dstCallsign, textMessage).getBytes(); } @Override diff --git a/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/message/TextMessage.java b/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/message/TextMessage.java index dcd18be..ac85273 100644 --- a/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/message/TextMessage.java +++ b/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/message/TextMessage.java @@ -7,15 +7,18 @@ public class TextMessage { public String dst; public String digipath; public String text; + public Integer ackId; public MessageItem toMessageItem(boolean isTransmit) { MessageItem messageItem = new MessageItem(); messageItem.setTimestampEpoch(System.currentTimeMillis()); - messageItem.setNeedsAck(false); // TODO messageItem.setIsTransmit(isTransmit); messageItem.setSrcCallsign(this.src); messageItem.setDstCallsign(this.dst); messageItem.setMessage(this.text); + messageItem.setAckId(this.ackId); + messageItem.setIsAcknowledged(false); + messageItem.setRetryCnt(0); return messageItem; } } diff --git a/codec2talkie/src/main/java/com/radio/codec2talkie/storage/AppDatabase.java b/codec2talkie/src/main/java/com/radio/codec2talkie/storage/AppDatabase.java index 400f561..9d9eaf5 100644 --- a/codec2talkie/src/main/java/com/radio/codec2talkie/storage/AppDatabase.java +++ b/codec2talkie/src/main/java/com/radio/codec2talkie/storage/AppDatabase.java @@ -20,7 +20,7 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @androidx.room.Database( - version = 11, + version = 12, entities = {LogItem.class, MessageItem.class, PositionItem.class, StationItem.class}, exportSchema = false ) diff --git a/codec2talkie/src/main/java/com/radio/codec2talkie/storage/message/MessageItem.java b/codec2talkie/src/main/java/com/radio/codec2talkie/storage/message/MessageItem.java index d01aa73..2896e54 100644 --- a/codec2talkie/src/main/java/com/radio/codec2talkie/storage/message/MessageItem.java +++ b/codec2talkie/src/main/java/com/radio/codec2talkie/storage/message/MessageItem.java @@ -4,7 +4,7 @@ import androidx.room.Entity; import androidx.room.Index; import androidx.room.PrimaryKey; -@Entity(indices = {@Index(value = {"id", "srcCallsign"}, unique = true)}) +@Entity(indices = {@Index(value = {"id", "srcCallsign", "ackId"}, unique = true)}) public class MessageItem { @PrimaryKey(autoGenerate = true) @@ -14,7 +14,9 @@ public class MessageItem { private String dstCallsign; private String message; private boolean needsAck; - private int ackNum; + private boolean isAcknowledged; + private int ackId; + private int retryCnt; private boolean isTransmit; public long getId() { @@ -35,7 +37,11 @@ public class MessageItem { public boolean getNeedsAck() { return needsAck; } - public int getAckNum() { return ackNum; } + public int getAckId() { return ackId; } + + public int getRetryCnt() { return this.retryCnt; } + + public boolean getIsAcknowledged() { return this.isAcknowledged; } public boolean getIsTransmit() { return isTransmit; } @@ -43,6 +49,8 @@ public class MessageItem { this.id = id; } + public void setRetryCnt(int retryCnt) { this.retryCnt = retryCnt; } + public void setTimestampEpoch(long timestampEpoch) { this.timestampEpoch = timestampEpoch; } @@ -57,8 +65,10 @@ public class MessageItem { public void setNeedsAck(boolean needsAck) { this.needsAck = needsAck; } - public void setAckNum(int ackNum) { this.ackNum = ackNum; } + public void setAckId(int ackId) { this.ackId = ackId; } public void setIsTransmit(boolean isTransmit) { this.isTransmit = isTransmit; } + + public void setIsAcknowledged(boolean isAcknowledged) { this.isAcknowledged = isAcknowledged; } } diff --git a/codec2talkie/src/main/java/com/radio/codec2talkie/storage/message/MessageItemActivity.java b/codec2talkie/src/main/java/com/radio/codec2talkie/storage/message/MessageItemActivity.java index 802161e..f0bbcfc 100644 --- a/codec2talkie/src/main/java/com/radio/codec2talkie/storage/message/MessageItemActivity.java +++ b/codec2talkie/src/main/java/com/radio/codec2talkie/storage/message/MessageItemActivity.java @@ -75,6 +75,7 @@ public class MessageItemActivity extends AppCompatActivityWithServiceConnection TextMessage textMessage = new TextMessage(); textMessage.dst = _groupName; textMessage.text = messageEdit.getText().toString(); + textMessage.ackId = 0; getService().sendTextMessage(textMessage); messageEdit.setText(""); } diff --git a/codec2talkie/src/main/java/com/radio/codec2talkie/storage/message/group/MessageGroupDialogSendTo.java b/codec2talkie/src/main/java/com/radio/codec2talkie/storage/message/group/MessageGroupDialogSendTo.java index 17a07d4..e84ecc6 100644 --- a/codec2talkie/src/main/java/com/radio/codec2talkie/storage/message/group/MessageGroupDialogSendTo.java +++ b/codec2talkie/src/main/java/com/radio/codec2talkie/storage/message/group/MessageGroupDialogSendTo.java @@ -13,6 +13,8 @@ import com.radio.codec2talkie.R; import com.radio.codec2talkie.app.AppService; import com.radio.codec2talkie.protocol.message.TextMessage; +import java.util.Locale; + public class MessageGroupDialogSendTo extends AlertDialog implements View.OnClickListener { private final AppService _appService; @@ -44,8 +46,9 @@ public class MessageGroupDialogSendTo extends AlertDialog implements View.OnClic assert targetEdit != null; assert messageEdit != null; TextMessage textMessage = new TextMessage(); - textMessage.dst = targetEdit.getText().toString(); + textMessage.dst = targetEdit.getText().toString().toUpperCase(Locale.ROOT); textMessage.text = messageEdit.getText().toString(); + textMessage.ackId = 0; _appService.sendTextMessage(textMessage); dismiss(); } else if (id == R.id.send_message_to_btn_cancel) {