mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-20 22:23:37 +00:00
begin support for message status
This commit is contained in:
parent
d25223ba71
commit
67d95039bf
9 changed files with 105 additions and 69 deletions
57
app/src/main/java/com/geeksville/mesh/DataPacket.kt
Normal file
57
app/src/main/java/com/geeksville/mesh/DataPacket.kt
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
package com.geeksville.mesh
|
||||
|
||||
import android.os.Parcelable
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Parcelize
|
||||
enum class MessageStatus : Parcelable {
|
||||
UNKNOWN, // Not set for this message
|
||||
RECEIVED, // Came in from the mesh
|
||||
QUEUED, // Waiting to send to the mesh as soon as we connect to the device
|
||||
ENROUTE, // Delivered to the radio, but no ACK or NAK received
|
||||
DELIVERED // We received an ack
|
||||
}
|
||||
|
||||
/**
|
||||
* A parcelable version of the protobuf MeshPacket + Data subpacket.
|
||||
*/
|
||||
@Serializable
|
||||
@Parcelize
|
||||
data class DataPacket(
|
||||
val from: String, // a nodeID string
|
||||
val to: String, // a nodeID string
|
||||
val rxTime: Long, // msecs since 1970
|
||||
val id: Int,
|
||||
val dataType: Int,
|
||||
val bytes: ByteArray,
|
||||
val status: MessageStatus = MessageStatus.UNKNOWN
|
||||
) : Parcelable {
|
||||
|
||||
// Autogenerated comparision, because we have a byte array
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as DataPacket
|
||||
|
||||
if (from != other.from) return false
|
||||
if (to != other.to) return false
|
||||
if (rxTime != other.rxTime) return false
|
||||
if (id != other.id) return false
|
||||
if (dataType != other.dataType) return false
|
||||
if (!bytes.contentEquals(other.bytes)) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = from.hashCode()
|
||||
result = 31 * result + to.hashCode()
|
||||
result = 31 * result + rxTime.hashCode()
|
||||
result = 31 * result + id
|
||||
result = 31 * result + dataType
|
||||
result = 31 * result + bytes.contentHashCode()
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
|
@ -3,73 +3,22 @@ package com.geeksville.mesh
|
|||
import android.os.Parcelable
|
||||
import com.geeksville.mesh.ui.bearing
|
||||
import com.geeksville.mesh.ui.latLongToMeter
|
||||
import com.geeksville.util.anonymize
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
|
||||
/**
|
||||
* When printing strings to logs sometimes we want to print useful debugging information about users
|
||||
* or positions. But we don't want to leak things like usernames or locations. So this function
|
||||
* if given a string, will return a string which is a maximum of three characters long, taken from the tail
|
||||
* of the string. Which should effectively hide real usernames and locations,
|
||||
* but still let us see if values were zero, empty or different.
|
||||
*/
|
||||
val Any?.anonymized: String
|
||||
get() = if (this != null) ("..." + this.toString().takeLast(3)) else "null"
|
||||
|
||||
//
|
||||
// model objects that directly map to the corresponding protobufs
|
||||
//
|
||||
|
||||
/**
|
||||
* A parcelable version of the protobuf MeshPacket + Data subpacket.
|
||||
*/
|
||||
@Serializable
|
||||
@Parcelize
|
||||
data class DataPacket(
|
||||
val from: String, // a nodeID string
|
||||
val to: String, // a nodeID string
|
||||
val rxTime: Long, // msecs since 1970
|
||||
val id: Int,
|
||||
val dataType: Int,
|
||||
val bytes: ByteArray
|
||||
) : Parcelable {
|
||||
|
||||
// Autogenerated comparision, because we have a byte array
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as DataPacket
|
||||
|
||||
if (from != other.from) return false
|
||||
if (to != other.to) return false
|
||||
if (rxTime != other.rxTime) return false
|
||||
if (id != other.id) return false
|
||||
if (dataType != other.dataType) return false
|
||||
if (!bytes.contentEquals(other.bytes)) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = from.hashCode()
|
||||
result = 31 * result + to.hashCode()
|
||||
result = 31 * result + rxTime.hashCode()
|
||||
result = 31 * result + id
|
||||
result = 31 * result + dataType
|
||||
result = 31 * result + bytes.contentHashCode()
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
@Parcelize
|
||||
data class MeshUser(val id: String, val longName: String, val shortName: String) :
|
||||
Parcelable {
|
||||
|
||||
override fun toString(): String {
|
||||
return "MeshUser(id=${id.anonymized}, longName=${longName.anonymized}, shortName=${shortName.anonymized})"
|
||||
return "MeshUser(id=${id.anonymize}, longName=${longName.anonymize}, shortName=${shortName.anonymize})"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -106,7 +55,7 @@ data class Position(
|
|||
fun bearing(o: Position) = bearing(latitude, longitude, o.latitude, o.longitude)
|
||||
|
||||
override fun toString(): String {
|
||||
return "Position(lat=${latitude.anonymized}, lon=${longitude.anonymized}, alt=${altitude.anonymized}, time=${time})"
|
||||
return "Position(lat=${latitude.anonymize}, lon=${longitude.anonymize}, alt=${altitude.anonymize}, time=${time})"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1256,7 +1256,7 @@ class MeshService : Service(), Logging {
|
|||
* Set our owner with either the new or old API
|
||||
*/
|
||||
fun setOwner(myId: String?, longName: String, shortName: String) {
|
||||
debug("SetOwner $myId : ${longName.anonymized} : $shortName")
|
||||
debug("SetOwner $myId : ${longName.anonymize} : $shortName")
|
||||
|
||||
val user = MeshProtos.User.newBuilder().also {
|
||||
if (myId != null) // Only set the id if it was provided
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ import com.geeksville.android.Logging
|
|||
import com.geeksville.concurrent.DeferredExecution
|
||||
import com.geeksville.concurrent.handledLaunch
|
||||
import com.geeksville.mesh.IRadioInterfaceService
|
||||
import com.geeksville.mesh.anonymized
|
||||
import com.geeksville.util.anonymize
|
||||
import com.geeksville.util.exceptionReporter
|
||||
import com.geeksville.util.ignoreException
|
||||
import com.geeksville.util.toRemoteExceptions
|
||||
|
|
@ -191,7 +191,7 @@ class RadioInterfaceService : Service(), Logging {
|
|||
val address = getPrefs(context).getString(DEVADDR_KEY, null)
|
||||
|
||||
if (address != null && !allPaired.contains(address)) {
|
||||
warn("Ignoring stale bond to ${address.anonymized}")
|
||||
warn("Ignoring stale bond to ${address.anonymize}")
|
||||
null
|
||||
} else
|
||||
address
|
||||
|
|
@ -560,7 +560,7 @@ class RadioInterfaceService : Service(), Logging {
|
|||
// device is off/not connected)
|
||||
val device = getBluetoothAdapter(this)?.getRemoteDevice(address)
|
||||
if (device != null) {
|
||||
info("Creating radio interface service. device=${address.anonymized}")
|
||||
info("Creating radio interface service. device=${address.anonymize}")
|
||||
|
||||
// Note this constructor also does no comm
|
||||
val s = SafeBluetooth(this, device)
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ import android.view.View
|
|||
import android.view.ViewGroup
|
||||
import android.view.inputmethod.EditorInfo
|
||||
import android.widget.EditText
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
|
|
@ -15,6 +17,7 @@ import com.geeksville.mesh.R
|
|||
import com.geeksville.mesh.model.TextMessage
|
||||
import com.geeksville.mesh.model.UIViewModel
|
||||
import com.geeksville.mesh.service.MeshService
|
||||
import com.google.android.material.chip.Chip
|
||||
import kotlinx.android.synthetic.main.adapter_message_layout.view.*
|
||||
import kotlinx.android.synthetic.main.messages_fragment.*
|
||||
import java.text.SimpleDateFormat
|
||||
|
|
@ -38,9 +41,10 @@ class MessagesFragment : ScreenFragment("Messages"), Logging {
|
|||
// Provide a direct reference to each of the views within a data item
|
||||
// Used to cache the views within the item layout for fast access
|
||||
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
val username = itemView.username
|
||||
val messageText = itemView.messageText
|
||||
val messageTime = itemView.messageTime
|
||||
val username: Chip = itemView.username
|
||||
val messageText: TextView = itemView.messageText
|
||||
val messageTime: TextView = itemView.messageTime
|
||||
val messageStatusIcon: ImageView = itemView.messageStatusIcon
|
||||
}
|
||||
|
||||
private val messagesAdapter = object : RecyclerView.Adapter<ViewHolder>() {
|
||||
|
|
|
|||
|
|
@ -28,10 +28,10 @@ import com.geeksville.android.hideKeyboard
|
|||
import com.geeksville.concurrent.handledLaunch
|
||||
import com.geeksville.mesh.MainActivity
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.anonymized
|
||||
import com.geeksville.mesh.model.UIViewModel
|
||||
import com.geeksville.mesh.service.MeshService
|
||||
import com.geeksville.mesh.service.RadioInterfaceService
|
||||
import com.geeksville.util.anonymize
|
||||
import com.geeksville.util.exceptionReporter
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import kotlinx.android.synthetic.main.settings_fragment.*
|
||||
|
|
@ -102,7 +102,7 @@ class BTScanModel(app: Application) : AndroidViewModel(app), Logging {
|
|||
// val isSelected get() = macAddress == selectedMacAddr
|
||||
|
||||
override fun toString(): String {
|
||||
return "BTScanEntry(name=${name.anonymized}, addr=${macAddress.anonymized})"
|
||||
return "BTScanEntry(name=${name.anonymize}, addr=${macAddress.anonymize})"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -282,7 +282,7 @@ class BTScanModel(app: Application) : AndroidViewModel(app), Logging {
|
|||
|
||||
/// Change to a new macaddr selection, updating GUI and radio
|
||||
fun changeScanSelection(context: MainActivity, newAddr: String) {
|
||||
info("Changing BT device to ${newAddr.anonymized}")
|
||||
info("Changing BT device to ${newAddr.anonymize}")
|
||||
selectedMacAddr = newAddr
|
||||
changeDeviceSelection(context, newAddr)
|
||||
}
|
||||
|
|
|
|||
15
app/src/main/res/drawable/ic_twotone_cloud_24.xml
Normal file
15
app/src/main/res/drawable/ic_twotone_cloud_24.xml
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M19.21,12.04l-1.53,-0.11 -0.3,-1.5C16.88,7.86 14.62,6 12,6 9.94,6 8.08,7.14 7.12,8.96l-0.5,0.95 -1.07,0.11C3.53,10.24 2,11.95 2,14c0,2.21 1.79,4 4,4h13c1.65,0 3,-1.35 3,-3 0,-1.55 -1.22,-2.86 -2.79,-2.96z"
|
||||
android:strokeAlpha="0.3"
|
||||
android:fillAlpha="0.3"/>
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M19.35,10.04C18.67,6.59 15.64,4 12,4 9.11,4 6.6,5.64 5.35,8.04 2.34,8.36 0,10.91 0,14c0,3.31 2.69,6 6,6h13c2.76,0 5,-2.24 5,-5 0,-2.64 -2.05,-4.78 -4.65,-4.96zM19,18H6c-2.21,0 -4,-1.79 -4,-4 0,-2.05 1.53,-3.76 3.56,-3.97l1.07,-0.11 0.5,-0.95C8.08,7.14 9.94,6 12,6c2.62,0 4.88,1.86 5.39,4.43l0.3,1.5 1.53,0.11c1.56,0.1 2.78,1.41 2.78,2.96 0,1.65 -1.35,3 -3,3z"/>
|
||||
</vector>
|
||||
|
|
@ -1,11 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="4dp"
|
||||
android:clipToPadding="false">
|
||||
android:clipToPadding="false"
|
||||
android:contentDescription="Message delivery status"
|
||||
android:elevation="2dp">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
|
|
@ -40,9 +41,19 @@
|
|||
android:id="@+id/messageTime"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:text="3 min"
|
||||
app:layout_constraintTop_toBottomOf="@+id/username"
|
||||
tools:layout_editor_absoluteX="25dp" />
|
||||
app:layout_constraintStart_toStartOf="@+id/username"
|
||||
app:layout_constraintTop_toBottomOf="@+id/username" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/messageStatusIcon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="8dp"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/messageText"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:srcCompat="@drawable/cloud_on" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit 81fee94de954fa4d8d942fff8fb929b9ef981a2f
|
||||
Subproject commit 90cf0a03708d5cf8a564a8044e0ab2ced6ccef52
|
||||
Loading…
Add table
Add a link
Reference in a new issue