diff --git a/res/values/strings.xml b/res/values/strings.xml
index be09ad2..3c887fe 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -240,6 +240,7 @@
UDP (send only)
Bluetooth SPP
+Bluetooth Low Energy
TCP/IP
USB Serial
@@ -360,7 +361,7 @@
Connected: %s
Messaging Preferences
-Set Messaging Retry & Ack Options
+Advanced Messaging Options
Message Retries
Number of messages to retry
Message retry limit
@@ -369,13 +370,19 @@
Retry interval start rate
Rate doubles each retry
-Ack Dupe Timeout
+Ack Dupe Timeout or Disable
Ack dupe timeout (0 = ack disabled)
-Dupe timeout
+Dupe acks allowed after timeout
Ack Dupe Timeout
-Enable
+Sends dupe acks after the timeout period
+Duplicate Messages
+Allows dupe messages after timeout period
+
+Duplicate messages allowed after timeout
+Dupe message timeout (0 = disabled)
+Dupe Message Timeout
APRS digi path
diff --git a/res/xml/messaging.xml b/res/xml/messaging.xml
index 942ebda..bdaf756 100644
--- a/res/xml/messaging.xml
+++ b/res/xml/messaging.xml
@@ -34,5 +34,20 @@
android:summary="@string/p_ackdupe_interval_summary"
android:dialogTitle="@string/p_ackdupe_interval_entry" />
+
+
+
+
diff --git a/src/MessagingPrefs.scala b/src/MessagingPrefs.scala
index 05ede96..ef6f09f 100644
--- a/src/MessagingPrefs.scala
+++ b/src/MessagingPrefs.scala
@@ -30,7 +30,7 @@ class MessagingPrefs extends PreferenceActivity with OnSharedPreferenceChangeLis
// Called when a shared preference is changed
override def onSharedPreferenceChanged(sp: SharedPreferences, key: String): Unit = {
key match {
- case "p.messaging" | "p.retry" | "p.ackdupetoggle" | "p.ackdupe" =>
+ case "p.messaging" | "p.retry" | "p.ackdupetoggle" | "p.ackdupe" | "p.msgdupetoggle" | "p.msgdupetime" =>
setPreferenceScreen(null) // Clear the current preference screen
loadXml() // Reload the preferences to reflect any changes
case _ => // Ignore other keys
diff --git a/src/PrefsWrapper.scala b/src/PrefsWrapper.scala
index 60ce981..ef59f45 100644
--- a/src/PrefsWrapper.scala
+++ b/src/PrefsWrapper.scala
@@ -21,6 +21,9 @@ class PrefsWrapper(val context : Context) {
def isAckDupeEnabled(): Boolean = {
prefs.getBoolean("p.ackdupetoggle", false)
}
+ def isMsgDupeEnabled(): Boolean = {
+ prefs.getBoolean("p.msgdupetoggle", false)
+ }
def isMetric(): Boolean = {
prefs.getString("p.units", "1") == "1" // "1" for metric, "2" for imperial
}
diff --git a/src/StorageDatabase.scala b/src/StorageDatabase.scala
index dc9600b..101a05e 100644
--- a/src/StorageDatabase.scala
+++ b/src/StorageDatabase.scala
@@ -7,6 +7,7 @@ import _root_.android.database.sqlite.SQLiteDatabase
import _root_.android.database.Cursor
import _root_.android.util.Log
import _root_.android.widget.FilterQueryProvider
+import _root_.android.preference.PreferenceManager
import _root_.net.ab0oo.aprs.parser._
@@ -183,6 +184,8 @@ class StorageDatabase(context : Context) extends
null, StorageDatabase.DB_VERSION) {
import StorageDatabase._
+ lazy val prefs = new PrefsWrapper(context)
+
override def onCreate(db: SQLiteDatabase) {
Log.d(TAG, "onCreate(): creating new database " + DB_NAME);
db.execSQL(Post.TABLE_CREATE);
@@ -259,21 +262,47 @@ class StorageDatabase(context : Context) extends
getWritableDatabase().replaceOrThrow(TABLE, CALL, cv)
}
- def isMessageDuplicate(call : String, msgid : String, text : String) : Boolean = {
- val c = getReadableDatabase().query(Message.TABLE, Message.COLUMNS,
- "type = 1 AND call = ? AND msgid = ? AND text = ?",
- Array(call, msgid, text),
- null, null,
- null, null)
- val result = (c.getCount() > 0)
- c.close()
- result
+ def isMessageDuplicate(call: String, msgid: String, text: String, currentTs: Long): Boolean = {
+ // Prepare the base query conditions
+ val selection = "type = 1 AND call = ? AND msgid = ? AND text = ?"
+ val selectionArgs = Array(call, msgid, text)
+ var query: String = selection
+ var args: Array[String] = selectionArgs
+
+ // If message duplication is enabled, add the time threshold condition
+ if (prefs.isMsgDupeEnabled) {
+ val msgDupetime = prefs.getStringInt("p.msgdupetime", 30)
+ val timeThreshold = currentTs - (msgDupetime * 1000)
+ // If msgDupetime is 0, return false immediately (no duplication check)
+ if (msgDupetime == 0) {
+ return false
+ }
+ query += " AND ts >= ?"
+ args = args :+ timeThreshold.toString
+ }
+
+ // Perform the database query with the constructed query and args
+ val cursor = getReadableDatabase().query(
+ Message.TABLE,
+ Message.COLUMNS,
+ query,
+ args,
+ null, null, null, null
+ )
+
+ // Check if there are any results (cursor.getCount() could be replaced with cursor.moveToFirst())
+ val result = cursor.moveToFirst() // Checks if at least one record is found
+ cursor.close()
+
+ result
}
- // add an incoming message, returns false if duplicate
- def addMessage(ts : Long, srccall : String, msg : MessagePacket) : Boolean = {
+
+ // Add an incoming message, returns false if duplicate
+ def addMessage(ts: Long, srccall: String, msg: MessagePacket): Boolean = {
import Message._
- if (isMessageDuplicate(srccall, msg.getMessageNumber(), msg.getMessageBody())) {
+ // Check if the message is a duplicate considering timestamp
+ if (isMessageDuplicate(srccall, msg.getMessageNumber(), msg.getMessageBody(), ts)) {
Log.i(TAG, "received duplicate message from %s: %s".format(srccall, msg))
return false
}